File indexing completed on 2026-04-09 07:48:51
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 """
0022
0023 Cubic Bezier polynomial interpolating from P0 to P3 as u
0024 goes from 0 to 1 controlled by P1, P2::
0025
0026 B(u) = P0*(1-u)**3 + P1*3*u*(1-u)**2 + P2*3*u**2*(1-u) + P3*u**3
0027
0028 To apply to surface of revolution (rr,z) in range z1 to z2, equate
0029
0030 (z - z1)
0031 u = -------- u = 0 at z=z1 u = 1 at z=z2
0032 (z2 - z1)
0033
0034 Or more in spirit of Bezier decide on begin/end points and
0035 control points
0036
0037 ::
0038
0039 (z1, rr1)
0040 (cz1, crr1)
0041 (cz2, crr2)
0042 (z2, rr2)
0043
0044
0045 * https://stackoverflow.com/questions/246525/how-can-i-draw-a-bezier-curve-using-pythons-pil
0046
0047
0048 ::
0049
0050 In [6]: bezier([0,1])
0051 Out[6]: [(50, 100), (100, 50)]
0052
0053 In [7]: bezier([0,0.5,1])
0054 Out[7]: [(50, 100), (77.5, 77.5), (100, 50)]
0055
0056 In [8]: bezier([0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1])
0057 Out[8]:
0058 [(50, 100),
0059 (55.900000000000006, 95.9),
0060 (61.60000000000001, 91.60000000000002),
0061 (67.1, 87.1),
0062 (72.4, 82.4),
0063 (77.5, 77.5),
0064 (82.4, 72.4),
0065 (87.1, 67.1),
0066 (91.60000000000001, 61.6),
0067 (95.89999999999999, 55.9),
0068 (100, 50)]
0069
0070
0071 """
0072
0073
0074
0075 def pascal_row(n):
0076
0077 result = [1]
0078 x, numerator = 1, n
0079 for denominator in range(1, n//2+1):
0080
0081 x *= numerator
0082 x /= denominator
0083 result.append(x)
0084 numerator -= 1
0085 if n&1 == 0:
0086
0087 result.extend(reversed(result[:-1]))
0088 else:
0089 result.extend(reversed(result))
0090 return result
0091
0092
0093 def make_bezier(xys):
0094 """
0095 :param xys: sequence of 2-tuples (Bezier control points)
0096 :return func: call it over t parameter iterable
0097
0098 Uses the generalized formula for bezier curves
0099 http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
0100
0101 For cubic bezier with 4 points combinations is just (1,3,3,1)
0102 """
0103 n = len(xys)
0104 combinations = pascal_row(n-1)
0105
0106 def bezier(ts):
0107 result = []
0108 for t in ts:
0109 tpowers = (t**i for i in range(n))
0110 upowers = reversed([(1-t)**i for i in range(n)])
0111 coefs = [c*a*b for c, a, b in zip(combinations, tpowers, upowers)]
0112 result.append(
0113 tuple(sum([coef*p for coef, p in zip(coefs, ps)]) for ps in zip(*xys)))
0114 return result
0115 pass
0116 return bezier
0117
0118
0119
0120
0121 from PIL import Image
0122 from PIL import ImageDraw
0123
0124
0125 def bezier_heart():
0126 im = Image.new('RGBA', (100, 100), (0, 0, 0, 0))
0127 draw = ImageDraw.Draw(im)
0128 ts = [t/100.0 for t in range(101)]
0129
0130 xys = [(50, 100), (80, 80), (100, 50)]
0131 bezier = make_bezier(xys)
0132 points = bezier(ts)
0133
0134 xys = [(100, 50), (100, 0), (50, 0), (50, 35)]
0135 bezier = make_bezier(xys)
0136 points.extend(bezier(ts))
0137
0138 xys = [(50, 35), (50, 0), (0, 0), (0, 50)]
0139 bezier = make_bezier(xys)
0140 points.extend(bezier(ts))
0141
0142 xys = [(0, 50), (20, 80), (50, 100)]
0143 bezier = make_bezier(xys)
0144 points.extend(bezier(ts))
0145
0146 draw.polygon(points, fill = 'red')
0147 im.save('out.png')
0148
0149
0150
0151 if __name__ == '__main__':
0152
0153 xys = [(50,100), (80,80), (100,50) ]
0154 bezier = make_bezier(xys)
0155
0156