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.
@@ -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