Skip to content

Commit 7ffdd6b

Browse files
fix: trapezoid signature change and internal checks (#1354)
Co-authored-by: pyansys-ci-bot <92810346+pyansys-ci-bot@users.noreply.github.com>
1 parent 3c5dc91 commit 7ffdd6b

File tree

6 files changed

+127
-60
lines changed

6 files changed

+127
-60
lines changed

.github/workflows/ci_cd.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ env:
1717
ANSRV_GEO_PORT: 700
1818
ANSRV_GEO_LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }}
1919
GEO_CONT_NAME: ans_geo
20-
RESET_IMAGE_CACHE: 2
20+
RESET_IMAGE_CACHE: 3
2121
IS_WORKFLOW_RUNNING: True
2222
ARTIFACTORY_VERSION: v251
2323
MEILISEARCH_API_KEY: ${{ secrets.MEILISEARCH_API_KEY }}

doc/changelog.d/1354.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
trapezoid signature change and internal checks

src/ansys/geometry/core/sketch/sketch.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -582,40 +582,49 @@ def triangle(
582582

583583
def trapezoid(
584584
self,
585-
width: Quantity | Distance | Real,
585+
base_width: Quantity | Distance | Real,
586586
height: Quantity | Distance | Real,
587-
slant_angle: Quantity | Angle | Real,
588-
nonsymmetrical_slant_angle: Quantity | Angle | Real | None = None,
587+
base_angle: Quantity | Angle | Real,
588+
base_asymmetric_angle: Quantity | Angle | Real | None = None,
589589
center: Point2D = ZERO_POINT2D,
590590
angle: Quantity | Angle | Real = 0,
591591
tag: str | None = None,
592592
) -> "Sketch":
593-
"""Add a triangle to the sketch using given vertex points.
593+
"""Add a trapezoid to the sketch using given vertex points.
594594
595595
Parameters
596596
----------
597-
width : ~pint.Quantity | Distance | Real
598-
Width of the slot main body.
597+
base_width : ~pint.Quantity | Distance | Real
598+
Width of the lower base of the trapezoid.
599599
height : ~pint.Quantity | Distance | Real
600600
Height of the slot.
601-
slant_angle : ~pint.Quantity | Distance | Real
602-
Angle for trapezoid generation.
603-
nonsymmetrical_slant_angle : ~pint.Quantity | Angle | Real | None, default: None
604-
Asymmetrical slant angles on each side of the trapezoid.
605-
The default is ``None``, in which case the trapezoid is symmetrical.
606-
center : Point2D, default: (0, 0)
601+
base_angle : ~pint.Quantity | Distance | Real
602+
Angle for trapezoid generation. Represents the angle
603+
on the base of the trapezoid.
604+
base_asymmetric_angle : ~pint.Quantity | Angle | Real | None, default: None
605+
Asymmetrical angles on each side of the trapezoid.
606+
The default is ``None``, in which case the trapezoid is symmetrical. If
607+
provided, the trapezoid is asymmetrical and the right corner angle
608+
at the base of the trapezoid is set to the provided value.
609+
center: Point2D, default: ZERO_POINT2D
607610
Center point of the trapezoid.
608611
angle : ~pint.Quantity | Angle | Real, default: 0
609612
Placement angle for orientation alignment.
610613
tag : str, default: None
611614
User-defined label for identifying the face.
612615
616+
Notes
617+
-----
618+
If an asymmetric base angle is defined, the base angle is
619+
applied to the left-most angle, and the asymmetric base angle
620+
is applied to the right-most angle.
621+
613622
Returns
614623
-------
615624
Sketch
616625
Revised sketch state ready for further sketch actions.
617626
"""
618-
trapezoid = Trapezoid(width, height, slant_angle, nonsymmetrical_slant_angle, center, angle)
627+
trapezoid = Trapezoid(base_width, height, base_angle, base_asymmetric_angle, center, angle)
619628
return self.face(trapezoid, tag)
620629

621630
def circle(

src/ansys/geometry/core/sketch/trapezoid.py

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import pyvista as pv
2828
from scipy.spatial.transform import Rotation as SpatialRotation
2929

30+
from ansys.geometry.core.logger import LOG
3031
from ansys.geometry.core.math.constants import ZERO_POINT2D
3132
from ansys.geometry.core.math.matrix import Matrix33
3233
from ansys.geometry.core.math.point import Point2D
@@ -42,45 +43,57 @@ class Trapezoid(SketchFace):
4243
4344
Parameters
4445
----------
45-
width : ~pint.Quantity | Distance | Real
46-
Width of the trapezoid.
46+
base_width : ~pint.Quantity | Distance | Real
47+
Width of the lower base of the trapezoid.
4748
height : ~pint.Quantity | Distance | Real
48-
Height of the trapezoid.
49-
slant_angle : ~pint.Quantity | Angle | Real
50-
Angle for trapezoid generation.
51-
nonsymmetrical_slant_angle : ~pint.Quantity | Angle | Real | None, default: None
52-
Asymmetrical slant angles on each side of the trapezoid.
53-
The default is ``None``, in which case the trapezoid is symmetrical.
49+
Height of the slot.
50+
base_angle : ~pint.Quantity | Distance | Real
51+
Angle for trapezoid generation. Represents the angle
52+
on the base of the trapezoid.
53+
base_asymmetric_angle : ~pint.Quantity | Angle | Real | None, default: None
54+
Asymmetrical angles on each side of the trapezoid.
55+
The default is ``None``, in which case the trapezoid is symmetrical. If
56+
provided, the trapezoid is asymmetrical and the right corner angle
57+
at the base of the trapezoid is set to the provided value.
5458
center: Point2D, default: ZERO_POINT2D
5559
Center point of the trapezoid.
5660
angle : ~pint.Quantity | Angle | Real, default: 0
5761
Placement angle for orientation alignment.
5862
5963
Notes
6064
-----
61-
If a nonsymmetrical slant angle is defined, the slant angle is
62-
applied to the left-most angle, and the nonsymmetrical slant angle
65+
If an asymmetric base angle is defined, the base angle is
66+
applied to the left-most angle, and the asymmetric base angle
6367
is applied to the right-most angle.
6468
"""
6569

6670
@check_input_types
6771
def __init__(
6872
self,
69-
width: Quantity | Distance | Real,
73+
base_width: Quantity | Distance | Real,
7074
height: Quantity | Distance | Real,
71-
slant_angle: Quantity | Angle | Real,
72-
nonsymmetrical_slant_angle: Quantity | Angle | Real | None = None,
75+
base_angle: Quantity | Angle | Real,
76+
base_asymmetric_angle: Quantity | Angle | Real | None = None,
7377
center: Point2D = ZERO_POINT2D,
7478
angle: Quantity | Angle | Real = 0,
7579
):
7680
"""Initialize the trapezoid."""
7781
super().__init__()
7882

83+
# TODO: Remove this warning in the next major release (v0.8.0)
84+
# https://github.com/ansys/pyansys-geometry/issues/1359
85+
LOG.warning(
86+
"The signature of the Trapezoid class has changed starting on"
87+
"version 0.7.X. Please refer to the documentation for more information."
88+
)
89+
7990
self._center = center
80-
self._width = width if isinstance(width, Distance) else Distance(width, center.unit)
81-
if self._width.value <= 0:
82-
raise ValueError("Width must be a real positive value.")
83-
width_magnitude = self._width.value.m_as(center.unit)
91+
self._base_width = (
92+
base_width if isinstance(base_width, Distance) else Distance(base_width, center.unit)
93+
)
94+
if self._base_width.value <= 0:
95+
raise ValueError("Base width must be a real positive value.")
96+
width_magnitude = self._base_width.value.m_as(center.unit)
8497

8598
self._height = height if isinstance(height, Distance) else Distance(height, center.unit)
8699
if self._height.value <= 0:
@@ -91,21 +104,42 @@ def __init__(
91104
angle = Angle(angle, DEFAULT_UNITS.ANGLE)
92105
angle = angle if isinstance(angle, Angle) else Angle(angle, angle.units)
93106

94-
if isinstance(slant_angle, (int, float)):
95-
slant_angle = Angle(slant_angle, DEFAULT_UNITS.ANGLE)
96-
slant_angle = (
97-
slant_angle if isinstance(slant_angle, Angle) else Angle(slant_angle, slant_angle.units)
107+
if isinstance(base_angle, (int, float)):
108+
base_angle = Angle(base_angle, DEFAULT_UNITS.ANGLE)
109+
base_angle = (
110+
base_angle if isinstance(base_angle, Angle) else Angle(base_angle, base_angle.units)
98111
)
99112

100-
if nonsymmetrical_slant_angle is None:
101-
nonsymmetrical_slant_angle = slant_angle
113+
if base_asymmetric_angle is None:
114+
base_asymmetric_angle = base_angle
102115
else:
103-
if isinstance(nonsymmetrical_slant_angle, (int, float)):
104-
nonsymmetrical_slant_angle = Angle(nonsymmetrical_slant_angle, DEFAULT_UNITS.ANGLE)
105-
nonsymmetrical_slant_angle = (
106-
nonsymmetrical_slant_angle
107-
if isinstance(nonsymmetrical_slant_angle, Angle)
108-
else Angle(nonsymmetrical_slant_angle, nonsymmetrical_slant_angle.units)
116+
if isinstance(base_asymmetric_angle, (int, float)):
117+
base_asymmetric_angle = Angle(base_asymmetric_angle, DEFAULT_UNITS.ANGLE)
118+
base_asymmetric_angle = (
119+
base_asymmetric_angle
120+
if isinstance(base_asymmetric_angle, Angle)
121+
else Angle(base_asymmetric_angle, base_asymmetric_angle.units)
122+
)
123+
124+
# SANITY CHECK: Ensure that the angles are valid (i.e. between 0 and 180 degrees)
125+
for trapz_angle in [base_angle, base_asymmetric_angle]:
126+
if (
127+
trapz_angle.value.m_as(UNITS.radian) < 0
128+
or trapz_angle.value.m_as(UNITS.radian) > np.pi
129+
):
130+
raise ValueError("The trapezoid angles must be between 0 and 180 degrees.")
131+
132+
# Check that the sum of both angles is larger than 90 degrees
133+
base_offset_right = height_magnitude / np.tan(
134+
base_asymmetric_angle.value.m_as(UNITS.radian)
135+
)
136+
base_offset_left = height_magnitude / np.tan(base_angle.value.m_as(UNITS.radian))
137+
138+
# SANITY CHECK: Ensure that the trapezoid is not degenerate
139+
if base_offset_right + base_offset_left >= width_magnitude:
140+
raise ValueError(
141+
"The trapezoid is degenerate. "
142+
"The provided angles, width and height do not form a valid trapezoid."
109143
)
110144

111145
rotation = Matrix33(
@@ -119,14 +153,12 @@ def __init__(
119153
rotated_point_1 = rotation @ [center.x.m - half_w, center.y.m - half_h, 0]
120154
rotated_point_2 = rotation @ [center.x.m + half_w, center.y.m - half_h, 0]
121155
rotated_point_3 = rotation @ [
122-
center.x.m - half_w + height_magnitude / np.tan(slant_angle.value.m_as(UNITS.radian)),
156+
center.x.m + half_w - base_offset_right,
123157
center.y.m + half_h,
124158
0,
125159
]
126160
rotated_point_4 = rotation @ [
127-
center.x.m
128-
+ half_w
129-
- height_magnitude / np.tan(nonsymmetrical_slant_angle.value.m_as(UNITS.radian)),
161+
center.x.m - half_w + base_offset_left,
130162
center.y.m + half_h,
131163
0,
132164
]
@@ -154,9 +186,9 @@ def center(self) -> Point2D:
154186
return self._center
155187

156188
@property
157-
def width(self) -> Quantity:
189+
def base_width(self) -> Quantity:
158190
"""Width of the trapezoid."""
159-
return self._width.value
191+
return self._base_width.value
160192

161193
@property
162194
def height(self) -> Quantity:

tests/integration/test_plotter.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,24 @@ def test_plot_trapezoid(verify_image_cache):
365365
sketch = Sketch()
366366

367367
# Create a trapezoid and plot
368-
sketch.trapezoid(10, 8, np.pi / 4, np.pi / 8, Point2D([10, -10]), tag="Trapezoid")
368+
sketch.trapezoid(10, 2, np.pi / 4, np.pi / 8, Point2D([10, -10]), tag="Trapezoid")
369369
sketch.select("Trapezoid")
370370
sketch.plot_selection(view_2d=True, screenshot=Path(IMAGE_RESULTS_DIR, "plot_trapezoid.png"))
371371

372372

373+
def test_plot_trapezoid_symmetric(verify_image_cache):
374+
"""Test plotting of a trapezoid which is symmetric."""
375+
# Create a sketch instance
376+
sketch = Sketch()
377+
378+
# Create a trapezoid and plot
379+
sketch.trapezoid(10, 2, np.pi / 4, tag="Trapezoid-symmetric")
380+
sketch.select("Trapezoid-symmetric")
381+
sketch.plot_selection(
382+
view_2d=True, screenshot=Path(IMAGE_RESULTS_DIR, "plot_trapezoid_sym.png")
383+
)
384+
385+
373386
@skip_no_xserver
374387
def test_plot_circle(verify_image_cache):
375388
"""Test plotting of a circle."""
@@ -601,14 +614,14 @@ def test_visualization_polydata():
601614
assert triangle.visualization_polydata.n_open_edges == 3
602615

603616
# Test for trapezoid visualization polydata
604-
trapezoid = Trapezoid(10, 8, np.pi / 4, np.pi / 8, Point2D([10, -10]))
617+
trapezoid = Trapezoid(10, 2, np.pi / 4, np.pi / 8, Point2D([10, -10]))
605618
assert trapezoid.visualization_polydata.center == pytest.approx(
606-
([5.34314575050762, -10.0, 0.0]),
619+
([10.0, -10.0, 0.0]),
607620
rel=1e-6,
608621
abs=1e-8,
609622
)
610623
assert trapezoid.visualization_polydata.bounds == pytest.approx(
611-
[-4.313708498984759, 15.0, -14.0, -6.0, 0.0, 0.0],
624+
[5.0, 15.0, -11.0, -9.0, 0.0, 0.0],
612625
rel=1e-6,
613626
abs=1e-8,
614627
)

tests/test_sketch.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,16 +237,16 @@ def test_sketch_trapezoidal_face():
237237
sketch = Sketch()
238238

239239
# Create the sketch face with trapezoid
240-
sketch.trapezoid(10, 8, np.pi / 4, np.pi / 8, Point2D([10, -10]), tag="trapezoid1")
240+
sketch.trapezoid(10, 1, np.pi / 4, np.pi / 8, Point2D([10, -10]), tag="trapezoid1")
241241
assert len(sketch.faces) == 1
242-
assert sketch.faces[0].width.m == 10
243-
assert sketch.faces[0].height.m == 8
242+
assert sketch.faces[0].base_width.m == 10
243+
assert sketch.faces[0].height.m == 1
244244
assert sketch.faces[0].center == Point2D([10, -10])
245245

246-
sketch.trapezoid(20, 10, np.pi / 8, np.pi / 16, Point2D([10, -10]), np.pi / 2, tag="trapezoid2")
246+
sketch.trapezoid(20, 2, np.pi / 8, np.pi / 16, Point2D([10, -10]), np.pi / 2, tag="trapezoid2")
247247
assert len(sketch.faces) == 2
248-
assert sketch.faces[1].width.m == 20
249-
assert sketch.faces[1].height.m == 10
248+
assert sketch.faces[1].base_width.m == 20
249+
assert sketch.faces[1].height.m == 2
250250
assert sketch.faces[1].center == Point2D([10, -10])
251251

252252
trapezoid1_retrieved = sketch.get("trapezoid1")
@@ -258,7 +258,7 @@ def test_sketch_trapezoidal_face():
258258
assert trapezoid2_retrieved[0] == sketch.faces[1]
259259

260260
# Test the trapezoid errors
261-
with pytest.raises(ValueError, match="Width must be a real positive value."):
261+
with pytest.raises(ValueError, match="Base width must be a real positive value."):
262262
sketch.trapezoid(
263263
0, 10, np.pi / 8, np.pi / 16, Point2D([10, -10]), np.pi / 2, tag="trapezoid3"
264264
)
@@ -268,6 +268,18 @@ def test_sketch_trapezoidal_face():
268268
10, -10, np.pi / 8, np.pi / 16, Point2D([10, -10]), np.pi / 2, tag="trapezoid3"
269269
)
270270

271+
with pytest.raises(ValueError, match="The trapezoid angles must be between 0 and 180 degrees."):
272+
sketch.trapezoid(
273+
10, 10, -np.pi, np.pi / 16, Point2D([10, -10]), np.pi / 2, tag="trapezoid3"
274+
)
275+
276+
with pytest.raises(
277+
ValueError,
278+
match="The trapezoid is degenerate. "
279+
"The provided angles, width and height do not form a valid trapezoid.",
280+
):
281+
sketch.trapezoid(10, 10, np.pi / 4, np.pi / 8, Point2D([10, -10]), tag="trapezoid3")
282+
271283

272284
def test_sketch_circle_instance():
273285
"""Test circle instance."""

0 commit comments

Comments
 (0)