Skip to content

Commit 5a36a07

Browse files
RobPasMuepyansys-ci-botPipKat
authored
feat: revolve a sketch given an axis and an origin (#1248)
Co-authored-by: pyansys-ci-bot <pyansys.github.bot@ansys.com> Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com>
1 parent 8ccf301 commit 5a36a07

File tree

11 files changed

+334
-10
lines changed

11 files changed

+334
-10
lines changed

.github/workflows/nightly_docker_test.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ jobs:
4444
uses: actions/setup-python@v5
4545
with:
4646
python-version: ${{ env.MAIN_PYTHON_VERSION }}
47-
cache: 'pip'
48-
cache-dependency-path: 'pyproject.toml'
4947

5048
- name: Set up headless display
5149
uses: pyvista/setup-headless-display-action@v2

doc/changelog.d/1248.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
feat: revolve a sketch given an axis and an origin
39.9 KB
Loading

doc/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def intersphinx_pyansys_geometry(switcher_version: str):
269269
"examples/03_modeling/boolean_operations": "_static/thumbnails/boolean_operations.png",
270270
"examples/03_modeling/scale_map_mirror_bodies": "_static/thumbnails/scale_map_mirror_bodies.png", # noqa: E501
271271
"examples/03_modeling/sweep_chain_profile": "_static/thumbnails/sweep_chain_profile.png",
272+
"examples/03_modeling/revolving": "_static/thumbnails/revolving.png",
272273
"examples/03_modeling/export_design": "_static/thumbnails/export_design.png",
273274
"examples/04_applied/01_naca_airfoils": "_static/thumbnails/naca_airfoils.png",
274275
"examples/04_applied/02_naca_fluent": "_static/thumbnails/naca_fluent.png",

doc/source/examples.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ These examples demonstrate service-based modeling operations.
4040
examples/03_modeling/boolean_operations.mystnb
4141
examples/03_modeling/scale_map_mirror_bodies.mystnb
4242
examples/03_modeling/sweep_chain_profile.mystnb
43+
examples/03_modeling/revolving.mystnb
4344
examples/03_modeling/export_design.mystnb
4445

4546
Applied examples
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
jupytext:
3+
text_representation:
4+
extension: .mystnb
5+
format_name: myst
6+
format_version: 0.13
7+
jupytext_version: 1.14.1
8+
kernelspec:
9+
display_name: Python 3 (ipykernel)
10+
language: python
11+
name: python3
12+
---
13+
14+
# Modeling: Revolving a sketch
15+
16+
This example shows how to use the ``revolve_sketch()`` method to
17+
revolve a sketch around an axis to create a 3D body. You can also
18+
specify the angle of revolution to create a partial body.
19+
20+
```{code-cell} ipython3
21+
# Imports
22+
from ansys.geometry.core import Modeler
23+
from ansys.geometry.core.math import (
24+
Plane,
25+
Point2D,
26+
Point3D,
27+
UNITVECTOR3D_X,
28+
UNITVECTOR3D_Z,
29+
)
30+
from ansys.geometry.core.misc import UNITS, Angle
31+
from ansys.geometry.core.sketch import Sketch
32+
33+
```
34+
35+
```{code-cell} ipython3
36+
# Initialize the modeler for this example notebook
37+
m = Modeler()
38+
```
39+
40+
## Example: Creating a quarter of a donut
41+
42+
The following code snippets show how to use the ``revolve_sketch()`` function to create a
43+
quarter of a 3D donut. The process involves defining a quarter of a circle as a profile
44+
and then revolving it around the Z-axis to create a 3D body.
45+
46+
### Initialize the sketch design
47+
48+
Create a design sketch named ``quarter-donut``.
49+
50+
```{code-cell} ipython3
51+
# Initialize the donut sketch design
52+
design = m.create_design("quarter-donut")
53+
```
54+
55+
### Define circle parameters
56+
57+
Set ``path_radius``, which represents the radius of the circular path that the profile
58+
circle sweeps along, to ``5`` units.
59+
Set ``profile_radius``, which represents the radius of the profile circle that sweeps
60+
along the path to create the donut body, to ``2`` units.
61+
62+
```{code-cell} ipython3
63+
# Donut parameters
64+
path_radius = 5
65+
profile_radius = 2
66+
```
67+
68+
### Create the profile circle
69+
70+
Create a circle on the XZ plane centered at the coordinates ``(5, 0, 0)``
71+
and use``profile_radius`` to define the radius. This circle serves as the
72+
profile or cross-sectional shape of the donut.
73+
74+
```{code-cell} ipython3
75+
# Create the circular profile on the XZ-plane centered at (5, 0, 0)
76+
# with a radius of 2
77+
plane_profile = Plane(
78+
origin=Point3D([path_radius, 0, 0]),
79+
direction_x=UNITVECTOR3D_X,
80+
direction_y=UNITVECTOR3D_Z,
81+
)
82+
profile = Sketch(plane=plane_profile)
83+
profile.circle(Point2D([0, 0]), profile_radius)
84+
85+
profile.plot()
86+
```
87+
88+
### Perform the revolve operation
89+
90+
Revolve the profile circle around the Z axis to create a quarter of a donut body.
91+
Set the angle of revolution to 90 degrees in the default direction, which is counterclockwise.
92+
93+
```{code-cell} ipython3
94+
# Revolve the profile around the Z axis and center in the absolute origin
95+
# for an angle of 90 degrees
96+
design.revolve_sketch(
97+
"quarter-donut-body",
98+
sketch=profile,
99+
axis=UNITVECTOR3D_Z,
100+
angle=Angle(90, unit=UNITS.degrees),
101+
rotation_origin=Point3D([0, 0, 0]),
102+
)
103+
104+
design.plot()
105+
```
106+
107+
### Perform a revolve operation with a negative angle of revolution
108+
109+
You can use a negative angle of revolution to create a quarter of a donut in the opposite direction. The following code snippet shows how to create a quarter of a donut in the clockwise direction. The same profile circle is used, but the angle of revolution is set to -90 degrees.
110+
111+
```{code-cell} ipython3
112+
# Initialize the donut sketch design
113+
design = m.create_design("quarter-donut-negative")
114+
115+
# Revolve the profile around the Z axis and center in the absolute origin
116+
# for an angle of -90 degrees (clockwise)
117+
design.revolve_sketch(
118+
"quarter-donut-body-negative",
119+
sketch=profile,
120+
axis=UNITVECTOR3D_Z,
121+
angle=Angle(-90, unit=UNITS.degrees),
122+
rotation_origin=Point3D([0, 0, 0]),
123+
)
124+
125+
design.plot()
126+
```

doc/source/examples/03_modeling/sweep_chain_profile.mystnb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ defined by ``profile_radius``. This circle serves as the profile or cross-sectio
7575
shape of the donut.
7676

7777
```{code-cell} ipython3
78-
# Create the circlular profile on the XZ-plane centered at (5, 0, 0)
78+
# Create the circular profile on the XZ plane centered at (5, 0, 0)
7979
# with a radius of 2
8080
plane_profile = Plane(direction_x=UNITVECTOR3D_X, direction_y=UNITVECTOR3D_Z)
8181
profile = Sketch(plane=plane_profile)
@@ -90,7 +90,7 @@ Another circle, representing the path along which the profile circle is swept, i
9090
the XY-plane centered at (0, 0, 0). The radius of this circle is defined by ``path_radius``.
9191

9292
```{code-cell} ipython3
93-
# Create the circlular path on the XY-plane centered at (0, 0, 0) with radius 5
93+
# Create the circular path on the XY plane centered at (0, 0, 0) with radius 5
9494
path = [Circle(Point3D([0, 0, 0]), path_radius).trim(Interval(0, 2 * np.pi))]
9595
```
9696

src/ansys/geometry/core/designer/component.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@
7373
from ansys.geometry.core.math.vector import UnitVector3D, Vector3D
7474
from ansys.geometry.core.misc.checks import ensure_design_is_active, min_backend_version
7575
from ansys.geometry.core.misc.measurements import DEFAULT_UNITS, Angle, Distance
76+
from ansys.geometry.core.shapes.curves.circle import Circle
7677
from ansys.geometry.core.shapes.curves.trimmed_curve import TrimmedCurve
78+
from ansys.geometry.core.shapes.parameterization import Interval
7779
from ansys.geometry.core.sketch.sketch import Sketch
7880
from ansys.geometry.core.typing import Real
7981

@@ -588,6 +590,76 @@ def sweep_chain(
588590
self._master_component.part.bodies.append(tb)
589591
return Body(response.id, response.name, self, tb)
590592

593+
@min_backend_version(24, 2, 0)
594+
@check_input_types
595+
def revolve_sketch(
596+
self,
597+
name: str,
598+
sketch: Sketch,
599+
axis: Vector3D,
600+
angle: Union[Quantity, Angle, Real],
601+
rotation_origin: Point3D,
602+
) -> Body:
603+
"""
604+
Create a solid body by revolving a sketch profile around an axis.
605+
606+
Notes
607+
-----
608+
It is important that the sketch plane origin is not coincident with the rotation
609+
origin. If the sketch plane origin is coincident with the rotation origin, the
610+
distance between the two points is zero, and the revolve operation fails.
611+
612+
Parameters
613+
----------
614+
name : str
615+
User-defined label for the new solid body.
616+
sketch : Sketch
617+
Two-dimensional sketch source for the revolve.
618+
axis : Vector3D
619+
Axis of rotation for the revolve.
620+
angle : Union[~pint.Quantity, Angle, Real]
621+
Angle to revolve the solid body around the axis. The angle can be positive or negative.
622+
rotation_origin : Point3D
623+
Origin of the axis of rotation.
624+
625+
Returns
626+
-------
627+
Body
628+
Revolved body from the given sketch.
629+
"""
630+
# Check that the sketch plane origin is not coincident with the rotation origin
631+
if sketch.plane.origin == rotation_origin:
632+
raise ValueError(
633+
"The sketch plane origin is coincident with the rotation origin. "
634+
+ "The distance between the points is zero, and the revolve operation will fail."
635+
)
636+
637+
# Compute the distance between the rotation origin and the sketch plane
638+
rotation_origin_to_sketch = sketch.plane.origin - rotation_origin
639+
rotation_origin_to_sketch_as_vector = Vector3D(rotation_origin_to_sketch)
640+
distance = Distance(
641+
rotation_origin_to_sketch_as_vector.norm,
642+
unit=rotation_origin_to_sketch.base_unit,
643+
)
644+
645+
# Define the revolve path
646+
circle = Circle(
647+
rotation_origin,
648+
radius=distance,
649+
reference=rotation_origin_to_sketch_as_vector,
650+
axis=axis,
651+
)
652+
angle = angle if isinstance(angle, Angle) else Angle(angle)
653+
interval = (
654+
Interval(0, angle.value.m_as(DEFAULT_UNITS.SERVER_ANGLE))
655+
if angle.value.m >= 0
656+
else Interval(angle.value.m_as(DEFAULT_UNITS.SERVER_ANGLE), 0)
657+
)
658+
path = circle.trim(interval)
659+
660+
# Create the revolved body by delegating to the sweep method
661+
return self.sweep_sketch(name, sketch, [path])
662+
591663
@protect_grpc
592664
@check_input_types
593665
@ensure_design_is_active

tests/integration/image_cache/results/.gitignore

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/integration/test_design.py

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@
5454
UnitVector3D,
5555
Vector3D,
5656
)
57-
from ansys.geometry.core.misc import DEFAULT_UNITS, UNITS, Accuracy, Distance
58-
from ansys.geometry.core.shapes.curves.circle import Circle
59-
from ansys.geometry.core.shapes.curves.ellipse import Ellipse
60-
from ansys.geometry.core.shapes.parameterization import Interval, ParamUV
57+
from ansys.geometry.core.misc import DEFAULT_UNITS, UNITS, Accuracy, Angle, Distance
58+
from ansys.geometry.core.shapes import Circle, Ellipse, Interval, ParamUV
6159
from ansys.geometry.core.sketch import Sketch
6260

6361
from .conftest import FILES_DIR, skip_if_linux
@@ -2286,3 +2284,60 @@ def test_create_body_from_loft_profile(modeler: Modeler):
22862284
# check volume of body
22872285
# expected is 0 since it's not a closed surface
22882286
assert result.volume.m == 0
2287+
2288+
2289+
def test_revolve_sketch(modeler: Modeler):
2290+
"""Test revolving a circular profile for a quarter donut."""
2291+
# Initialize the donut sketch design
2292+
design = modeler.create_design("quarter-donut")
2293+
2294+
# Donut parameters
2295+
path_radius = 5
2296+
profile_radius = 2
2297+
2298+
# Create the circular profile on the XZ plane centered at (5, 0, 0)
2299+
# with a radius of 2
2300+
plane_profile = Plane(
2301+
origin=Point3D([path_radius, 0, 0]), direction_x=UNITVECTOR3D_X, direction_y=UNITVECTOR3D_Z
2302+
)
2303+
profile = Sketch(plane=plane_profile)
2304+
profile.circle(Point2D([0, 0]), profile_radius)
2305+
2306+
# Revolve the profile around the Z axis and center in the absolute origin
2307+
# for an angle of 90 degrees
2308+
body = design.revolve_sketch(
2309+
"donut-body",
2310+
sketch=profile,
2311+
axis=UNITVECTOR3D_Z,
2312+
angle=Angle(90, unit=UNITS.degrees),
2313+
rotation_origin=Point3D([0, 0, 0]),
2314+
)
2315+
2316+
assert body.is_surface == False
2317+
assert body.name == "donut-body"
2318+
assert np.isclose(body.volume.m, np.pi**2 * 2 * 5, rtol=1e-3) # quarter of a torus volume
2319+
2320+
2321+
def test_revolve_sketch_fail(modeler: Modeler):
2322+
"""Test demonstrating the failure of revolving a sketch when it is located in the
2323+
same origin."""
2324+
# Initialize the donut sketch design
2325+
design = modeler.create_design("revolve-fail")
2326+
2327+
# Create an XZ plane centered at (0, 0, 0)
2328+
plane_profile = Plane(
2329+
origin=Point3D([0, 0, 0]), direction_x=UNITVECTOR3D_X, direction_y=UNITVECTOR3D_Z
2330+
)
2331+
profile = Sketch(plane=plane_profile)
2332+
2333+
# Try revolving the profile...
2334+
with pytest.raises(
2335+
ValueError, match="The sketch plane origin is coincident with the rotation origin."
2336+
):
2337+
design.revolve_sketch(
2338+
"donut-body",
2339+
sketch=profile,
2340+
axis=UNITVECTOR3D_Z,
2341+
angle=Angle(90, unit=UNITS.degrees),
2342+
rotation_origin=Point3D([0, 0, 0]),
2343+
)

0 commit comments

Comments
 (0)