fontgeometry 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fontgeometry/__init__.py +1 -0
- fontgeometry/beziertools.py +357 -0
- fontgeometry/cubics.py +467 -0
- fontgeometry/extract.py +37 -0
- fontgeometry/ftbeziertools.py +142 -0
- fontgeometry/geometry.py +203 -0
- fontgeometry/geometryPoints.py +110 -0
- fontgeometry/py.typed +0 -0
- fontgeometry/typing.py +1 -0
- fontgeometry-0.4.0.dist-info/METADATA +51 -0
- fontgeometry-0.4.0.dist-info/RECORD +13 -0
- fontgeometry-0.4.0.dist-info/WHEEL +4 -0
- fontgeometry-0.4.0.dist-info/licenses/LICENSE +21 -0
fontgeometry/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Hello
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
from math import sqrt
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
|
|
4
|
+
from fontgeometry.ftbeziertools import (
|
|
5
|
+
calcCubicParameters,
|
|
6
|
+
calcQuadraticParameters,
|
|
7
|
+
epsilon,
|
|
8
|
+
solveQuadratic,
|
|
9
|
+
)
|
|
10
|
+
from fontgeometry.geometry import distance_between_points, half_point
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from fontgeometry.typing import PointTuple
|
|
14
|
+
|
|
15
|
+
# Adapted from robofab.pens.filterPen
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def estimateCubicCurveLength(
|
|
19
|
+
pt1: "PointTuple",
|
|
20
|
+
pt2: "PointTuple",
|
|
21
|
+
pt3: "PointTuple",
|
|
22
|
+
pt4: "PointTuple",
|
|
23
|
+
precision: int = 10,
|
|
24
|
+
) -> float:
|
|
25
|
+
"""
|
|
26
|
+
Estimate the length of this curve by iterating through it and averaging the length
|
|
27
|
+
of the flat bits.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
length = 0.0
|
|
31
|
+
step = 1.0 / precision
|
|
32
|
+
points = getPointListForCubic(
|
|
33
|
+
[f * step for f in range(precision + 1)], pt1, pt2, pt3, pt4
|
|
34
|
+
)
|
|
35
|
+
for i in range(len(points) - 1):
|
|
36
|
+
pta = points[i]
|
|
37
|
+
ptb = points[i + 1]
|
|
38
|
+
length += distance_between_points(pta, ptb)
|
|
39
|
+
return length
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def getPointOnCubic(
|
|
43
|
+
t: float, pt1: "PointTuple", pt2: "PointTuple", pt3: "PointTuple", pt4: "PointTuple"
|
|
44
|
+
) -> "PointTuple":
|
|
45
|
+
"""
|
|
46
|
+
Return the point for t on the cubic curve defined by pt1, pt2, pt3, pt4.
|
|
47
|
+
"""
|
|
48
|
+
if t == 0:
|
|
49
|
+
return pt1
|
|
50
|
+
if t == 1:
|
|
51
|
+
return pt4
|
|
52
|
+
if t == 0.5:
|
|
53
|
+
a = half_point(pt1, pt2)
|
|
54
|
+
b = half_point(pt2, pt3)
|
|
55
|
+
c = half_point(pt3, pt4)
|
|
56
|
+
d = half_point(a, b)
|
|
57
|
+
e = half_point(b, c)
|
|
58
|
+
return half_point(d, e)
|
|
59
|
+
else:
|
|
60
|
+
cx = (pt2[0] - pt1[0]) * 3
|
|
61
|
+
cy = (pt2[1] - pt1[1]) * 3
|
|
62
|
+
bx = (pt3[0] - pt2[0]) * 3 - cx
|
|
63
|
+
by = (pt3[1] - pt2[1]) * 3 - cy
|
|
64
|
+
ax = pt4[0] - pt1[0] - cx - bx
|
|
65
|
+
ay = pt4[1] - pt1[1] - cy - by
|
|
66
|
+
t3 = t**3
|
|
67
|
+
t2 = t * t
|
|
68
|
+
x = ax * t3 + bx * t2 + cx * t + pt1[0]
|
|
69
|
+
y = ay * t3 + by * t2 + cy * t + pt1[1]
|
|
70
|
+
return x, y
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def getPointListForCubic(
|
|
74
|
+
ts: list[float],
|
|
75
|
+
pt1: "PointTuple",
|
|
76
|
+
pt2: "PointTuple",
|
|
77
|
+
pt3: "PointTuple",
|
|
78
|
+
pt4: "PointTuple",
|
|
79
|
+
) -> "list[PointTuple]":
|
|
80
|
+
"""
|
|
81
|
+
Return a list of points for increments of t on the cubic curve defined by pt1, pt2,
|
|
82
|
+
pt3, pt4.
|
|
83
|
+
"""
|
|
84
|
+
(x0, y0), (x1, y1) = pt1, pt2
|
|
85
|
+
cx = (x1 - x0) * 3
|
|
86
|
+
cy = (y1 - y0) * 3
|
|
87
|
+
bx = (pt3[0] - x1) * 3 - cx
|
|
88
|
+
by = (pt3[1] - y1) * 3 - cy
|
|
89
|
+
ax = pt4[0] - x0 - cx - bx
|
|
90
|
+
ay = pt4[1] - y0 - cy - by
|
|
91
|
+
path: "list[PointTuple]" = []
|
|
92
|
+
for t in ts:
|
|
93
|
+
t3 = t**3
|
|
94
|
+
t2 = t * t
|
|
95
|
+
x = ax * t3 + bx * t2 + cx * t + x0
|
|
96
|
+
y = ay * t3 + by * t2 + cy * t + y0
|
|
97
|
+
path.append((x, y))
|
|
98
|
+
return path
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def getExtremaForCubic(
|
|
102
|
+
pt1: "PointTuple",
|
|
103
|
+
pt2: "PointTuple",
|
|
104
|
+
pt3: "PointTuple",
|
|
105
|
+
pt4: "PointTuple",
|
|
106
|
+
h: bool = True,
|
|
107
|
+
v: bool = False,
|
|
108
|
+
include_start_end: bool = False,
|
|
109
|
+
) -> list[float]:
|
|
110
|
+
"""
|
|
111
|
+
Return a list of t values at which the cubic curve defined by pt1, pt2, pt3, pt4 has
|
|
112
|
+
extrema.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
pt1 (PointTuple): The first point of the cubic
|
|
116
|
+
pt2 (PointTuple): The second point of the cubic, a control point
|
|
117
|
+
pt3 (PointTuple): The third point of the cubic, a control point
|
|
118
|
+
pt4 (PointTuple): The fourth point of the cubic
|
|
119
|
+
h (bool, optional): Calculate extrema for horizontal derivative == 0 (= what
|
|
120
|
+
type designers call vertical extrema!). Defaults to True.
|
|
121
|
+
v (bool, optional): Calculate extrema for vertical derivative == 0 (= what type
|
|
122
|
+
designers call horizontal extrema!). Defaults to False.
|
|
123
|
+
include_start_end (bool, optional): Whether to include extrema that lie at the
|
|
124
|
+
start or end point of the curve. Defaults to False.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
list[PointTuple]: The list of t values.
|
|
128
|
+
"""
|
|
129
|
+
(ax, ay), (bx, by), c, _d = calcCubicParameters(pt1, pt2, pt3, pt4)
|
|
130
|
+
ax *= 3.0
|
|
131
|
+
ay *= 3.0
|
|
132
|
+
bx *= 2.0
|
|
133
|
+
by *= 2.0
|
|
134
|
+
roots = []
|
|
135
|
+
if include_start_end:
|
|
136
|
+
if h:
|
|
137
|
+
roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 <= t <= 1]
|
|
138
|
+
if v:
|
|
139
|
+
roots += [t for t in solveQuadratic(ax, bx, c[0]) if 0 <= t <= 1]
|
|
140
|
+
else:
|
|
141
|
+
if h:
|
|
142
|
+
roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
|
|
143
|
+
if v:
|
|
144
|
+
roots += [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
|
|
145
|
+
return roots
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def getExtremumPointsForCubic(
|
|
149
|
+
pt1: "PointTuple",
|
|
150
|
+
pt2: "PointTuple",
|
|
151
|
+
pt3: "PointTuple",
|
|
152
|
+
pt4: "PointTuple",
|
|
153
|
+
h: bool = True,
|
|
154
|
+
v: bool = False,
|
|
155
|
+
include_start_end: bool = False,
|
|
156
|
+
) -> "list[PointTuple]":
|
|
157
|
+
"""
|
|
158
|
+
Return a list of points as (x, y) tuples at which the cubic curve defined by pt1,
|
|
159
|
+
pt2, pt3, pt4 has extrema.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
pt1 (PointTuple): The first point of the cubic
|
|
163
|
+
pt2 (PointTuple): The second point of the cubic, a control point
|
|
164
|
+
pt3 (PointTuple): The third point of the cubic, a control point
|
|
165
|
+
pt4 (PointTuple): The fourth point of the cubic
|
|
166
|
+
h (bool, optional): Calculate extrema for horizontal derivative == 0 (= what
|
|
167
|
+
type designers call vertical extrema!). Defaults to True.
|
|
168
|
+
v (bool, optional): Calculate extrema for vertical derivative == 0 (= what type
|
|
169
|
+
designers call horizontal extrema!). Defaults to False.
|
|
170
|
+
include_start_end (bool, optional): Whether to include extrema that lie at the
|
|
171
|
+
start or end point of the curve. Defaults to False.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
list[PointTuple]: The list of point tuples.
|
|
175
|
+
"""
|
|
176
|
+
return getPointListForCubic(
|
|
177
|
+
getExtremaForCubic(
|
|
178
|
+
pt1, pt2, pt3, pt4, h=h, v=v, include_start_end=include_start_end
|
|
179
|
+
),
|
|
180
|
+
pt1,
|
|
181
|
+
pt2,
|
|
182
|
+
pt3,
|
|
183
|
+
pt4,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def getInflectionsForCubic(
|
|
188
|
+
pt1: "PointTuple", pt2: "PointTuple", pt3: "PointTuple", pt4: "PointTuple"
|
|
189
|
+
) -> list[float]:
|
|
190
|
+
"""
|
|
191
|
+
Return a list of t values at which the cubic curve defined by pt1, pt2, pt3, pt4 has
|
|
192
|
+
inflections.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
pt1 (PointTuple): The first point of the cubic
|
|
196
|
+
pt2 (PointTuple): The second point of the cubic, a control point
|
|
197
|
+
pt3 (PointTuple): The third point of the cubic, a control point
|
|
198
|
+
pt4 (PointTuple): The fourth point of the cubic
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
list[float]: _description_
|
|
202
|
+
"""
|
|
203
|
+
# After https://github.com/mekkablue/InsertInflections
|
|
204
|
+
roots: list[float] = []
|
|
205
|
+
|
|
206
|
+
x1, y1 = pt1
|
|
207
|
+
x2, y2 = pt2
|
|
208
|
+
x3, y3 = pt3
|
|
209
|
+
x4, y4 = pt4
|
|
210
|
+
|
|
211
|
+
ax = x2 - x1
|
|
212
|
+
ay = y2 - y1
|
|
213
|
+
bx = x3 - x2 - ax
|
|
214
|
+
by = y3 - y2 - ay
|
|
215
|
+
cx = x4 - x3 - ax - bx - bx
|
|
216
|
+
cy = y4 - y3 - ay - by - by
|
|
217
|
+
|
|
218
|
+
c0 = (ax * by) - (ay * bx)
|
|
219
|
+
c1 = (ax * cy) - (ay * cx)
|
|
220
|
+
c2 = (bx * cy) - (by * cx)
|
|
221
|
+
|
|
222
|
+
if abs(c2) > 0.00001:
|
|
223
|
+
discr = (c1**2) - (4 * c0 * c2)
|
|
224
|
+
c2 *= 2
|
|
225
|
+
if abs(discr) < 0.000001:
|
|
226
|
+
root = -c1 / c2
|
|
227
|
+
if (root > 0.001) and (root < 0.99):
|
|
228
|
+
roots.append(root)
|
|
229
|
+
elif discr > 0:
|
|
230
|
+
discr = discr**0.5
|
|
231
|
+
root = (-c1 - discr) / c2
|
|
232
|
+
if (root > 0.001) and (root < 0.99):
|
|
233
|
+
roots.append(root)
|
|
234
|
+
|
|
235
|
+
root = (-c1 + discr) / c2
|
|
236
|
+
if (root > 0.001) and (root < 0.99):
|
|
237
|
+
roots.append(root)
|
|
238
|
+
elif c1 != 0.0:
|
|
239
|
+
root = -c0 / c1
|
|
240
|
+
if (root > 0.001) and (root < 0.99):
|
|
241
|
+
roots.append(root)
|
|
242
|
+
|
|
243
|
+
return roots
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def getPointListForQuadratic(
|
|
247
|
+
ts: list[float], pt1: "PointTuple", pt2: "PointTuple", pt3: "PointTuple"
|
|
248
|
+
) -> "list[PointTuple]":
|
|
249
|
+
"""
|
|
250
|
+
Return a list of points for increments of t on the quadratic curve defined by pt1,
|
|
251
|
+
pt2, pt3.
|
|
252
|
+
"""
|
|
253
|
+
(x0, y0), (x1, y1), (x2, y2) = pt1, pt2, pt3
|
|
254
|
+
path: "list[PointTuple]" = []
|
|
255
|
+
for t in ts:
|
|
256
|
+
t0 = (1 - t) * (1 - t)
|
|
257
|
+
t1 = 2 * (1 - t) * t
|
|
258
|
+
t2 = t * t
|
|
259
|
+
x = t0 * x0 + t1 * x1 + t2 * x2
|
|
260
|
+
y = t0 * y0 + t1 * y1 + t2 * y2
|
|
261
|
+
path.append((x, y))
|
|
262
|
+
return path
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def getExtremaForQuadratic(
|
|
266
|
+
pt1: "PointTuple",
|
|
267
|
+
pt2: "PointTuple",
|
|
268
|
+
pt3: "PointTuple",
|
|
269
|
+
h: bool = True,
|
|
270
|
+
v: bool = False,
|
|
271
|
+
include_start_end: bool = False,
|
|
272
|
+
) -> list[float]:
|
|
273
|
+
"""
|
|
274
|
+
Return a list of t values at which the quadratic curve defined by pt1, pt2, pt3, pt4
|
|
275
|
+
has extrema.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
pt1 (PointTuple): The first point of the curve
|
|
279
|
+
pt2 (PointTuple): The second point of the curve, an offcurve point
|
|
280
|
+
pt3 (PointTuple): The third point of the curve
|
|
281
|
+
h (bool, optional): Calculate extrema for horizontal derivative == 0 (= what
|
|
282
|
+
type designers call vertical extrema!). Defaults to True.
|
|
283
|
+
v (bool, optional): Calculate extrema for vertical derivative == 0 (= what type
|
|
284
|
+
designers call horizontal extrema!). Defaults to False.
|
|
285
|
+
include_start_end (bool, optional): Whether to include extrema that lie at the
|
|
286
|
+
start or end point of the curve. Defaults to False.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
list[PointTuple]: The list of t values.
|
|
290
|
+
"""
|
|
291
|
+
(ax, ay), (bx, by), _c = calcQuadraticParameters(pt1, pt2, pt3)
|
|
292
|
+
ax *= 2.0
|
|
293
|
+
ay *= 2.0
|
|
294
|
+
roots = []
|
|
295
|
+
if include_start_end:
|
|
296
|
+
if h:
|
|
297
|
+
roots = [t for t in solveLinear(ay, by) if 0 <= t <= 1]
|
|
298
|
+
if v:
|
|
299
|
+
roots += [t for t in solveLinear(ax, bx) if 0 <= t <= 1]
|
|
300
|
+
else:
|
|
301
|
+
if h:
|
|
302
|
+
roots = [t for t in solveLinear(ay, by) if 0 < t < 1]
|
|
303
|
+
if v:
|
|
304
|
+
roots += [t for t in solveLinear(ax, bx) if 0 < t < 1]
|
|
305
|
+
return roots
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def getExtremumPointsForQuadratic(
|
|
309
|
+
pt1: "PointTuple",
|
|
310
|
+
pt2: "PointTuple",
|
|
311
|
+
pt3: "PointTuple",
|
|
312
|
+
h: bool = True,
|
|
313
|
+
v: bool = False,
|
|
314
|
+
include_start_end: bool = False,
|
|
315
|
+
) -> "list[PointTuple]":
|
|
316
|
+
"""
|
|
317
|
+
Return a list of points as (x, y) tuples at which the quadratic curve defined by
|
|
318
|
+
pt1, pt2, pt3, pt4 has extrema.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
pt1 (PointTuple): The first point of the curve
|
|
322
|
+
pt2 (PointTuple): The second point of the curve, an offcurve point
|
|
323
|
+
pt3 (PointTuple): The third point of the curve
|
|
324
|
+
h (bool, optional): Calculate extrema for horizontal derivative == 0 (= what
|
|
325
|
+
type designers call vertical extrema!). Defaults to True.
|
|
326
|
+
v (bool, optional): Calculate extrema for vertical derivative == 0 (= what type
|
|
327
|
+
designers call horizontal extrema!). Defaults to False.
|
|
328
|
+
include_start_end (bool, optional): Whether to include extrema that lie at the
|
|
329
|
+
start or end point of the curve. Defaults to False.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
list[PointTuple]: The list of point tuples.
|
|
333
|
+
"""
|
|
334
|
+
return getPointListForQuadratic(
|
|
335
|
+
getExtremaForQuadratic(
|
|
336
|
+
pt1, pt2, pt3, h=h, v=v, include_start_end=include_start_end
|
|
337
|
+
),
|
|
338
|
+
pt1,
|
|
339
|
+
pt2,
|
|
340
|
+
pt3,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def solveLinear(a: float, b: float) -> list[float]:
|
|
345
|
+
if abs(a) < epsilon:
|
|
346
|
+
if abs(b) < epsilon:
|
|
347
|
+
roots = []
|
|
348
|
+
else:
|
|
349
|
+
roots = [0.0]
|
|
350
|
+
else:
|
|
351
|
+
DD = b * b
|
|
352
|
+
if DD >= 0.0:
|
|
353
|
+
rDD = sqrt(DD)
|
|
354
|
+
roots = [(-b + rDD) / 2.0 / a, (-b - rDD) / 2.0 / a]
|
|
355
|
+
else:
|
|
356
|
+
roots = []
|
|
357
|
+
return roots
|