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: extrude existing edges to create surface bodies #1638

Merged
merged 6 commits into from
Jan 15, 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/1638.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extrude existing edges to create surface bodies
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ classifiers = [
]

dependencies = [
"ansys-api-geometry==0.4.25",
"ansys-api-geometry==0.4.27",
"ansys-tools-path>=0.3,<1",
"ansys-tools-visualization-interface>=0.2.6,<1",
"attrs!=24.3.0",
Expand Down
2 changes: 1 addition & 1 deletion src/ansys/geometry/core/designer/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ def _update_design_inplace(self) -> None:
# https://github.com/ansys/pyansys-geometry/issues/1319
#
self._components = []
self._bodies = []
self._clear_cached_bodies()
jonahrb marked this conversation as resolved.
Show resolved Hide resolved
self._materials = []
self._named_selections = {}
self._coordinate_systems = {}
Expand Down
147 changes: 145 additions & 2 deletions src/ansys/geometry/core/designer/geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

from ansys.api.geometry.v0.commands_pb2 import (
ChamferRequest,
ExtrudeEdgesRequest,
ExtrudeEdgesUpToRequest,
ExtrudeFacesRequest,
ExtrudeFacesUpToRequest,
FilletRequest,
Expand All @@ -39,7 +41,11 @@
)
from ansys.geometry.core.errors import protect_grpc
from ansys.geometry.core.math import Point3D, UnitVector3D
from ansys.geometry.core.misc.auxiliary import get_bodies_from_ids, get_design_from_face
from ansys.geometry.core.misc.auxiliary import (
get_bodies_from_ids,
get_design_from_edge,
get_design_from_face,
)
from ansys.geometry.core.misc.checks import (
check_is_float_int,
check_type_all_elements_in_iterable,
Expand Down Expand Up @@ -288,7 +294,7 @@ def extrude_faces_up_to(
seed_point : Point3D
Origin to define the extrusion.
direction : UnitVector3D, default: None
Direction of extrusion. If no direction is provided, it will be inferred.
Direction of extrusion.
extrude_type : ExtrudeType, default: ExtrudeType.ADD
Type of extrusion to be performed.
offset_mode : OffsetMode, default: OffsetMode.MOVE_FACES_TOGETHER
Expand Down Expand Up @@ -336,3 +342,140 @@ def extrude_faces_up_to(
else:
self._grpc_client.log.info("Failed to extrude faces.")
return []

@protect_grpc
@min_backend_version(25, 2, 0)
def extrude_edges(
self,
edges: Union["Edge", List["Edge"]],
distance: Real,
from_face: "Face" = None,
from_point: Point3D = None,
direction: UnitVector3D = None,
extrude_type: ExtrudeType = ExtrudeType.ADD,
pull_symmetric: bool = False,
copy: bool = False,
natural_extension: bool = False,
) -> List["Body"]:
"""Extrude a selection of edges. Provide either a face or a direction and point.

Parameters
----------
edges : Edge | List[Edge]
Edges to extrude.
distance : Real
Distance to extrude.
from_face : Face, default: None
Face to pull normal from.
from_point : Point3D, default: None
Point to pull from. Must be used with ``direction``.
direction : UnitVector3D, default: None
Direction to pull. Must be used with ``from_point``.
extrude_type : ExtrudeType, default: ExtrudeType.ADD
Type of extrusion to be performed.
pull_symmetric : bool, default: False
Pull symmetrically on both sides if ``True``.
copy : bool, default: False
Copy the edge and move it instead of extruding the original edge if ``True``.
natural_extension : bool, default: False
Surfaces will extend in a natural or linear shape after exceeding its original range.

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

edges: list[Edge] = edges if isinstance(edges, list) else [edges]
check_type_all_elements_in_iterable(edges, Edge)
check_is_float_int(distance, "distance")
if from_face is None and None in (from_point, direction):
raise ValueError(
"To extrude edges, either a face or a direction and point must be provided."
)

for edge in edges:
edge.body._reset_tessellation_cache()

result = self._commands_stub.ExtrudeEdges(
ExtrudeEdgesRequest(
edges=[edge._grpc_id for edge in edges],
distance=distance,
face=from_face._grpc_id,
point=None if from_point is None else point3d_to_grpc_point(from_point),
direction=None if direction is None else unit_vector_to_grpc_direction(direction),
extrude_type=extrude_type.value,
pull_symmetric=pull_symmetric,
copy=copy,
natural_extension=natural_extension,
)
)

design = get_design_from_edge(edges[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 extrude edges.")
return []

@protect_grpc
@min_backend_version(25, 2, 0)
def extrude_edges_up_to(
self,
edges: Union["Edge", List["Edge"]],
up_to_selection: Union["Face", "Edge", "Body"],
seed_point: Point3D,
direction: UnitVector3D,
extrude_type: ExtrudeType = ExtrudeType.ADD,
) -> List["Body"]:
"""Extrude a selection of edges up to another object.

Parameters
----------
edges : Edge | List[Edge]
Edges to extrude.
up_to_selection : Face, default: None
The object to pull the faces up to.
seed_point : Point3D
Origin to define the extrusion.
direction : UnitVector3D, default: None
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.edge import Edge

edges: list[Edge] = edges if isinstance(edges, list) else [edges]
check_type_all_elements_in_iterable(edges, Edge)

for edge in edges:
edge.body._reset_tessellation_cache()

result = self._commands_stub.ExtrudeEdgesUpTo(
ExtrudeEdgesUpToRequest(
edges=[edge._grpc_id for edge in edges],
up_to_selection=up_to_selection._grpc_id,
seed_point=point3d_to_grpc_point(seed_point),
direction=unit_vector_to_grpc_direction(direction),
extrude_type=extrude_type.value,
)
)

design = get_design_from_edge(edges[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 extrude edges.")
return []
48 changes: 48 additions & 0 deletions tests/integration/test_geometry_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,51 @@ def test_extrude_faces_up_to(modeler: Modeler):
assert len(bodies) == 0
assert len(design.bodies) == 1
assert body.volume.m == pytest.approx(Quantity(5, UNITS.m**3).m, rel=1e-6, abs=1e-8)


def test_extrude_edges_and_up_to(modeler: Modeler):
design = modeler.create_design("extrude_edges")
upto = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1)
upto.translate(UnitVector3D([0, 0, 1]), 5)
assert upto.volume.m == pytest.approx(Quantity(1, UNITS.m**3).m, rel=1e-6, abs=1e-8)

body = design.extrude_sketch("box2", Sketch().box(Point2D([0, 0]), 1, 1), 1)
assert body.volume.m == pytest.approx(Quantity(1, UNITS.m**3).m, rel=1e-6, abs=1e-8)

# extrude edge
created_bodies = modeler.geometry_commands.extrude_edges(
body.edges[0], 1, body.edges[0].faces[1]
)
assert len(created_bodies) == 1
assert created_bodies[0].is_surface
assert created_bodies[0].faces[0].area.m == pytest.approx(
Quantity(1, UNITS.m**2).m, rel=1e-6, abs=1e-8
)

# extrude edge up to face
created_bodies = modeler.geometry_commands.extrude_edges_up_to(
body.edges[0], upto.faces[0], Point3D([0, 0, 0]), UnitVector3D([0, 0, 1])
)
assert len(created_bodies) == 1
assert created_bodies[0].is_surface
assert created_bodies[0].faces[0].area.m == pytest.approx(
Quantity(4, UNITS.m**2).m, rel=1e-6, abs=1e-8
)

# extrude multiple edges up to
created_bodies = modeler.geometry_commands.extrude_edges_up_to(
body.edges, upto.faces[1], Point3D([0, 0, 0]), UnitVector3D([0, 0, 1])
)
assert created_bodies[0].is_surface
assert created_bodies[0].faces[0].area.m == pytest.approx(
Quantity(6, UNITS.m**2).m, rel=1e-6, abs=1e-8
)
assert created_bodies[0].faces[1].area.m == pytest.approx(
Quantity(6, UNITS.m**2).m, rel=1e-6, abs=1e-8
)
assert created_bodies[0].faces[2].area.m == pytest.approx(
Quantity(6, UNITS.m**2).m, rel=1e-6, abs=1e-8
)
assert created_bodies[0].faces[3].area.m == pytest.approx(
Quantity(6, UNITS.m**2).m, rel=1e-6, abs=1e-8
)
Loading