用 PIL 绘制二次贝塞尔曲线
PIL(Python Image Library) 中有 line, ellipse, rectangle 等常规绘图的函数,但缺少绘制曲线的函数,今天我的需求中需要绘制一个二次贝塞尔曲线(两个端点,一个控制点)。无奈之下,到网上找了一个 c 语言写的算法,把它写成了 python 的,并且写成了类的形式。
#coding=utf-8
# 二次贝塞尔曲线绘图程序
# 木野狐(Neil Chen) http://rchen.cnblogs.com
# 2007-5-16
# 根据 c 程序改写为 python 类的形式,参考:
# http://hi.baidu.com/sunyante/blog/item/2627d911b3061810b8127b70.html
from math import sqrt
import Image, ImageDraw
class Bezier2(object):
def __init__(self, draw, points, line_width, line_color):
self.draw = draw
self.points = points
self.line_width = line_width
self.line_color = line_color
self.current_point = (0, 0)
def moveto(self, p):
self.current_point = p
def lineto(self, p):
self.draw.line((self.current_point, p), width=self.line_width, fill=self.line_color)
self.current_point = p
def render(self):
NO = 3
KT = 5
m = NO - 1
p = {} # p[3][2]
for i in range(0, NO, 2):
p[i] = self.points[i]
l1 = 1.0 * (self.points[0][0] - self.points[1][0])
ll = 1.0 * (self.points[0][1] - self.points[1][1])
l1 = sqrt(l1 * l1 + ll * ll)
l2 = 1.0 * (self.points[2][0] - self.points[1][0])
ll = 1.0 * (self.points[2][1] - self.points[1][1])
l2 = sqrt(l2 * l2 + ll * ll)
p[1] = (
((l1 + l2) * (l1 + l2) * self.points[1][0] - l2 * l2 * self.points[0][0] - l1 * l1 * self.points[2][0]) / (2 * l1 * l2),
((l1 + l2) * (l1 + l2) * self.points[1][1] - l2 * l2 * self.points[0][1] - l1 * l1 * self.points[2][1]) / (2 * l1 * l2)
)
'''
# 画出切线
self.moveto(p[0])
for i in range(1, m+1):
self.lineto(p[i])
'''
pk = {} # pk[129][2]
for i in range(m + 1):
pk[i] = p[i]
pt = {} # pt[129][2]
for k in range(KT + 1):
for i in range(0, m + 1, 2):
pt[2*i] = pk[i]
for i in range(m):
pt[2*i + 1] = (
int(pk[i][0] + pk[i+1][0]) >> 1,
int(pk[i][1] + pk[i+1][1]) >> 1
)
for i in range(1, m):
pt[2*i] = (
int(pt[2*i-1][0] + pt[2*i+1][0]) >> 1,
int(pt[2*i-1][1] + pt[2*i+1][1]) >> 1
)
for i in range(2*m + 1):
pk[i] = pt[i]
if k == KT:
break
m <<= 1
self.moveto(pk[0])
for i in range(1, 2*m + 1):
self.lineto(pk[i])
# 测试代码
if __name__ == '__main__':
im = Image.new('RGBA', (1024,1024), (255,255,255))
draw = ImageDraw.Draw(im)
points = ((150,300), (300,200), (485,300))
b = Bezier2(draw, points, 1, (0,0,255))
b.render()
points2 = ((150,300), (100,400), (485,300))
b2 = Bezier2(draw, points2, 3, (0,255,0))
b2.render()
del draw
im.show()
# 二次贝塞尔曲线绘图程序
# 木野狐(Neil Chen) http://rchen.cnblogs.com
# 2007-5-16
# 根据 c 程序改写为 python 类的形式,参考:
# http://hi.baidu.com/sunyante/blog/item/2627d911b3061810b8127b70.html
from math import sqrt
import Image, ImageDraw
class Bezier2(object):
def __init__(self, draw, points, line_width, line_color):
self.draw = draw
self.points = points
self.line_width = line_width
self.line_color = line_color
self.current_point = (0, 0)
def moveto(self, p):
self.current_point = p
def lineto(self, p):
self.draw.line((self.current_point, p), width=self.line_width, fill=self.line_color)
self.current_point = p
def render(self):
NO = 3
KT = 5
m = NO - 1
p = {} # p[3][2]
for i in range(0, NO, 2):
p[i] = self.points[i]
l1 = 1.0 * (self.points[0][0] - self.points[1][0])
ll = 1.0 * (self.points[0][1] - self.points[1][1])
l1 = sqrt(l1 * l1 + ll * ll)
l2 = 1.0 * (self.points[2][0] - self.points[1][0])
ll = 1.0 * (self.points[2][1] - self.points[1][1])
l2 = sqrt(l2 * l2 + ll * ll)
p[1] = (
((l1 + l2) * (l1 + l2) * self.points[1][0] - l2 * l2 * self.points[0][0] - l1 * l1 * self.points[2][0]) / (2 * l1 * l2),
((l1 + l2) * (l1 + l2) * self.points[1][1] - l2 * l2 * self.points[0][1] - l1 * l1 * self.points[2][1]) / (2 * l1 * l2)
)
'''
# 画出切线
self.moveto(p[0])
for i in range(1, m+1):
self.lineto(p[i])
'''
pk = {} # pk[129][2]
for i in range(m + 1):
pk[i] = p[i]
pt = {} # pt[129][2]
for k in range(KT + 1):
for i in range(0, m + 1, 2):
pt[2*i] = pk[i]
for i in range(m):
pt[2*i + 1] = (
int(pk[i][0] + pk[i+1][0]) >> 1,
int(pk[i][1] + pk[i+1][1]) >> 1
)
for i in range(1, m):
pt[2*i] = (
int(pt[2*i-1][0] + pt[2*i+1][0]) >> 1,
int(pt[2*i-1][1] + pt[2*i+1][1]) >> 1
)
for i in range(2*m + 1):
pk[i] = pt[i]
if k == KT:
break
m <<= 1
self.moveto(pk[0])
for i in range(1, 2*m + 1):
self.lineto(pk[i])
# 测试代码
if __name__ == '__main__':
im = Image.new('RGBA', (1024,1024), (255,255,255))
draw = ImageDraw.Draw(im)
points = ((150,300), (300,200), (485,300))
b = Bezier2(draw, points, 1, (0,0,255))
b.render()
points2 = ((150,300), (100,400), (485,300))
b2 = Bezier2(draw, points2, 3, (0,255,0))
b2.render()
del draw
im.show()