Skip to content

Commit 3efb080

Browse files
authored
polarArray start angle, and rotation fix (#1006) (#1016)
* polarArray start angle, and rotation fix (#1006) * * Add TOL module constant * Remove nested function
1 parent c2c2cc1 commit 3efb080

File tree

2 files changed

+55
-41
lines changed

2 files changed

+55
-41
lines changed

cadquery/cq.py

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
CQObject = Union[Vector, Location, Shape, Sketch]
6464
VectorLike = Union[Tuple[float, float], Tuple[float, float, float], Vector]
6565
CombineMode = Union[bool, Literal["cut", "a", "s"]] # a : additive, s: subtractive
66+
TOL = 1e-6
6667

6768
T = TypeVar("T", bound="Workplane")
6869
"""A type variable used to make the return type of a method the same as the
@@ -1486,51 +1487,42 @@ def polarArray(
14861487
rotate: bool = True,
14871488
) -> T:
14881489
"""
1489-
Creates an polar array of points and pushes them onto the stack.
1490-
The 0 degree reference angle is located along the local X-axis.
1490+
Creates a polar array of points and pushes them onto the stack.
1491+
The zero degree reference angle is located along the local X-axis.
14911492
14921493
:param radius: Radius of the array.
1493-
:param startAngle: Starting angle (degrees) of array. 0 degrees is
1494-
situated along local X-axis.
1494+
:param startAngle: Starting angle (degrees) of array. Zero degrees is
1495+
situated along the local X-axis.
14951496
:param angle: The angle (degrees) to fill with elements. A positive
14961497
value will fill in the counter-clockwise direction. If fill is
1497-
false, angle is the angle between elements.
1498-
:param count: Number of elements in array. ( > 0 )
1498+
False, angle is the angle between elements.
1499+
:param count: Number of elements in array. (count >= 1)
14991500
:param fill: Interpret the angle as total if True (default: True).
15001501
:param rotate: Rotate every item (default: True).
15011502
"""
15021503

1503-
if count <= 0:
1504-
raise ValueError("No elements in array")
1505-
1506-
# First element at start angle, convert to cartesian coords
1507-
x = radius * math.sin(math.radians(startAngle))
1508-
y = radius * math.cos(math.radians(startAngle))
1509-
1510-
if rotate:
1511-
loc = Location(Vector(x, y), Vector(0, 0, 1), -startAngle)
1512-
else:
1513-
loc = Location(Vector(x, y))
1514-
1515-
locs = [loc]
1504+
if count < 1:
1505+
raise ValueError(f"At least 1 element required, requested {count}")
15161506

15171507
# Calculate angle between elements
15181508
if fill:
1519-
if angle % 360 == 0:
1509+
if abs(math.remainder(angle, 360)) < TOL:
15201510
angle = angle / count
1521-
elif count > 1:
1511+
else:
15221512
# Inclusive start and end
1523-
angle = angle / (count - 1)
1513+
angle = angle / (count - 1) if count > 1 else startAngle
1514+
1515+
locs = []
15241516

1525-
# Add additional elements
1526-
for i in range(1, count):
1517+
# Add elements
1518+
for i in range(0, count):
15271519
phi_deg = startAngle + (angle * i)
15281520
phi = math.radians(phi_deg)
1529-
x = radius * math.sin(phi)
1530-
y = radius * math.cos(phi)
1521+
x = radius * math.cos(phi)
1522+
y = radius * math.sin(phi)
15311523

15321524
if rotate:
1533-
loc = Location(Vector(x, y), Vector(0, 0, 1), -phi_deg)
1525+
loc = Location(Vector(x, y), Vector(0, 0, 1), phi_deg)
15341526
else:
15351527
loc = Location(Vector(x, y))
15361528

tests/test_cadquery.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,12 +1392,6 @@ def testRectArray(self):
13921392
def testPolarArray(self):
13931393
radius = 10
13941394

1395-
# Test for proper number of elements
1396-
s = Workplane("XY").polarArray(radius, 0, 180, 1)
1397-
self.assertEqual(1, s.size())
1398-
s = Workplane("XY").polarArray(radius, 0, 180, 6)
1399-
self.assertEqual(6, s.size())
1400-
14011395
to_x = lambda l: l.wrapped.Transformation().TranslationPart().X()
14021396
to_y = lambda l: l.wrapped.Transformation().TranslationPart().Y()
14031397
to_angle = (
@@ -1408,23 +1402,27 @@ def testPolarArray(self):
14081402

14091403
# Test for proper placement when fill == True
14101404
s = Workplane("XY").polarArray(radius, 0, 180, 3)
1411-
self.assertAlmostEqual(0, to_y(s.objects[1]))
1412-
self.assertAlmostEqual(radius, to_x(s.objects[1]))
1405+
self.assertEqual(3, s.size())
1406+
self.assertAlmostEqual(radius, to_x(s.objects[0]))
1407+
self.assertAlmostEqual(0, to_y(s.objects[0]))
14131408

14141409
# Test for proper placement when angle to fill is multiple of 360 deg
14151410
s = Workplane("XY").polarArray(radius, 0, 360, 4)
1416-
self.assertAlmostEqual(0, to_y(s.objects[1]))
1417-
self.assertAlmostEqual(radius, to_x(s.objects[1]))
1411+
self.assertEqual(4, s.size())
1412+
self.assertAlmostEqual(radius, to_x(s.objects[0]))
1413+
self.assertAlmostEqual(0, to_y(s.objects[0]))
14181414

14191415
# Test for proper placement when fill == False
14201416
s = Workplane("XY").polarArray(radius, 0, 90, 3, fill=False)
1421-
self.assertAlmostEqual(0, to_y(s.objects[1]))
1422-
self.assertAlmostEqual(radius, to_x(s.objects[1]))
1417+
self.assertEqual(3, s.size())
1418+
self.assertAlmostEqual(-radius, to_x(s.objects[2]))
1419+
self.assertAlmostEqual(0, to_y(s.objects[2]))
14231420

14241421
# Test for proper operation of startAngle
14251422
s = Workplane("XY").polarArray(radius, 90, 180, 3)
1426-
self.assertAlmostEqual(radius, to_x(s.objects[0]))
1427-
self.assertAlmostEqual(0, to_y(s.objects[0]))
1423+
self.assertEqual(3, s.size())
1424+
self.assertAlmostEqual(0, to_x(s.objects[0]))
1425+
self.assertAlmostEqual(radius, to_y(s.objects[0]))
14281426

14291427
# Test for local rotation
14301428
s = Workplane().polarArray(radius, 0, 180, 3)
@@ -1435,6 +1433,21 @@ def testPolarArray(self):
14351433
self.assertAlmostEqual(0, to_angle(s.objects[0]))
14361434
self.assertAlmostEqual(0, to_angle(s.objects[1]))
14371435

1436+
with raises(ValueError):
1437+
Workplane().polarArray(radius, 20, 180, 0)
1438+
1439+
s = Workplane().polarArray(radius, 20, 0, 1)
1440+
assert s.size() == 1
1441+
assert Workplane().polarLine(radius, 20).val().positionAt(
1442+
1
1443+
).toTuple() == approx(s.val().toTuple()[0])
1444+
1445+
s = Workplane().center(2, -4).polarArray(2, 10, 50, 3).rect(1.0, 0.5).extrude(1)
1446+
assert s.solids().size() == 3
1447+
assert s.vertices(">Y and >Z").val().toTuple() == approx(
1448+
(3.0334936490538906, -1.7099364905389036, 1.0)
1449+
)
1450+
14381451
def testNestedCircle(self):
14391452
s = (
14401453
Workplane("XY")
@@ -2003,6 +2016,15 @@ def testPolarLines(self):
20032016
self.assertEqual(1, r.wires().size())
20042017
self.assertEqual(5, r.wires().edges().size())
20052018

2019+
r = Workplane().polarLineTo(1, 20)
2020+
assert r.val().positionAt(1).toTuple() == approx(
2021+
(0.9396926207859084, 0.3420201433256687, 0.0)
2022+
)
2023+
r = Workplane().move(1, 1).polarLine(1, 20)
2024+
assert r.val().positionAt(1).toTuple() == approx(
2025+
(1.9396926207859084, 1.3420201433256687, 0.0)
2026+
)
2027+
20062028
def testLargestDimension(self):
20072029
"""
20082030
Tests the largestDimension function when no solids are on the stack and when there are

0 commit comments

Comments
 (0)