Skip to content

Commit 3b05bc3

Browse files
authored
Merge pull request #63 from fonttools/pathpen-varargs
make PathPen curveTo/qCurveTo accept variable number of points
2 parents 703430e + 81d4bb4 commit 3b05bc3

File tree

4 files changed

+84
-9
lines changed

4 files changed

+84
-9
lines changed

src/python/pathops/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
PathOpsError,
1515
UnsupportedVerbError,
1616
OpenPathError,
17+
NumberOfPointsError,
1718
bits2float,
1819
float2bits,
1920
decompose_quadratic_segment,

src/python/pathops/_pathops.pxd

+1-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ cdef class PathPen:
247247

248248
cpdef lineTo(self, pt)
249249

250-
cpdef curveTo(self, pt1, pt2, pt3)
250+
# def curveTo(self, *points)
251251

252252
# def qCurveTo(self, *points)
253253

src/python/pathops/_pathops.pyx

+31-8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ cdef class OpenPathError(PathOpsError):
5454
pass
5555

5656

57+
cdef class NumberOfPointsError(PathOpsError):
58+
pass
59+
60+
5761
# Helpers to convert to/from a float and its bit pattern
5862

5963
cdef inline int32_t _float2bits(float x):
@@ -891,16 +895,35 @@ cdef class PathPen:
891895
cpdef lineTo(self, pt):
892896
self.path.lineTo(pt[0], pt[1])
893897

894-
cpdef curveTo(self, pt1, pt2, pt3):
895-
# support BasePen "super-beziers"? Nah.
896-
self.path.cubicTo(
897-
pt1[0], pt1[1],
898-
pt2[0], pt2[1],
899-
pt3[0], pt3[1])
898+
def curveTo(self, *points):
899+
num_offcurves = len(points) - 1
900+
if num_offcurves == 2:
901+
pt1, pt2, pt3 = points
902+
self.path.cubicTo(
903+
pt1[0], pt1[1],
904+
pt2[0], pt2[1],
905+
pt3[0], pt3[1])
906+
elif num_offcurves == 1:
907+
pt1, pt2 = points
908+
self.path.quadTo(pt1[0], pt1[1], pt2[0], pt2[1])
909+
elif num_offcurves == 0:
910+
pt = points[0]
911+
self.path.lineTo(pt[0], pt[1])
912+
else:
913+
# support BasePen "super-beziers"? Nah.
914+
raise NumberOfPointsError(
915+
"curveTo requires between 1 and 3 points; got %d" % len(points)
916+
)
900917

901918
def qCurveTo(self, *points):
902-
for pt1, pt2 in _decompose_quadratic_segment(points):
903-
self._qCurveToOne(pt1, pt2)
919+
num_offcurves = len(points) - 1
920+
if num_offcurves > 0:
921+
for pt1, pt2 in _decompose_quadratic_segment(points):
922+
self._qCurveToOne(pt1, pt2)
923+
elif num_offcurves == 0:
924+
self.lineTo(points[0])
925+
else:
926+
raise NumberOfPointsError("qCurveTo requires at least 1 point; got 0")
904927

905928
cdef _qCurveToOne(self, pt1, pt2):
906929
self.path.quadTo(pt1[0], pt1[1], pt2[0], pt2[1])

tests/pathops_test.py

+51
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
ArcSize,
1212
Direction,
1313
simplify,
14+
NumberOfPointsError,
1415
)
1516

1617
import pytest
@@ -106,6 +107,56 @@ def test_decompose_join_quadratic_segments(self):
106107
('qCurveTo', ((1.0, 1.0), (2.0, 2.0), (3.0, 3.0))),
107108
('closePath', ())]
108109

110+
def test_qCurveTo_varargs(self):
111+
path = Path()
112+
pen = path.getPen()
113+
pen.moveTo((0, 0))
114+
pen.qCurveTo((1, 1))
115+
pen.closePath()
116+
117+
items = list(path)
118+
assert len(items) == 3
119+
# qcurve without offcurves is stored internally as a line
120+
assert items[1][0] == PathVerb.LINE
121+
assert items[1][1] == ((1.0, 1.0),)
122+
123+
assert list(path.segments) == [
124+
('moveTo', ((0.0, 0.0),)),
125+
('lineTo', ((1.0, 1.0),)),
126+
('closePath', ()),
127+
]
128+
129+
with pytest.raises(
130+
NumberOfPointsError, match="qCurveTo requires at least 1 point; got 0"
131+
):
132+
pen.qCurveTo()
133+
134+
def test_curveTo_varargs(self):
135+
path = Path()
136+
pen = path.getPen()
137+
pen.moveTo((0, 0))
138+
pen.curveTo((1, 1), (2, 2), (3, 3)) # a cubic
139+
pen.curveTo((4, 4), (5, 5)) # a quadratic
140+
pen.curveTo((6, 6)) # a line
141+
pen.closePath()
142+
143+
assert list(path.segments) == [
144+
('moveTo', ((0.0, 0.0),)),
145+
('curveTo', ((1.0, 1.0), (2.0, 2.0), (3.0, 3.0))),
146+
('qCurveTo', ((4.0, 4.0), (5.0, 5.0))),
147+
('lineTo', ((6.0, 6.0),)),
148+
('closePath', ()),
149+
]
150+
151+
with pytest.raises(
152+
NumberOfPointsError, match="curveTo requires between 1 and 3 points; got 0"
153+
):
154+
pen.curveTo()
155+
with pytest.raises(
156+
NumberOfPointsError, match="curveTo requires between 1 and 3 points; got 4"
157+
):
158+
pen.curveTo((0, 0), (1, 1), (2, 2), (3, 3))
159+
109160
def test_last_implicit_lineTo(self):
110161
# https://github.com/fonttools/skia-pathops/issues/6
111162
path = Path()

0 commit comments

Comments
 (0)