Pythonでベジェ曲線クラスを実装してwxPythonで動かす
を読んでなるほどなーと思い、理解の確認でPythonでベジェ曲線のクラスを実装した。
#! /usr/bin/python2.7 # coding: utf-8 # bezier.py # ref: http://ruiueyama.tumblr.com/post/11197882224 def pt(p, q, t): assert 0 <= t <= 1 return [a + (b - a) * float(t) for a,b in zip(p, q)] def mid(p, q): return pt(p, q, .5) class Bezier: def __init__(self, ctrls, dt=3*1e-3): self.ctrls = ctrls self.dt = dt def __iter__(self): ctrl = self.ctrls dt = self.dt t = 0 while t <= 1: x,y = self.walkdown(ctrl, t) yield x,y t += dt def walkdown(self, ctrl, t): dt = self.dt if len(ctrl) == 1: return ctrl[0] else: ps = [pt(p, q, t) for p,q in zip(ctrl, ctrl[1:])] return self.walkdown(ps, t)
これだけ。ついでにwxPythonで曲線を描画してみた。gnuplotでやったほうがよかったかなー。
#!/usr/bin/python2.7 # coding: utf-8 import wx from Bezier import * class Mapper(object): # coordinate mapper: bounds -> client def __init__(self, size, lowerleft, upperright): self.size = size self.ll = lowerleft self.ur = upperright def toClient(self, x, y): w, h = self.size xs,ys = map(float, self.ll) xe,ye = map(float, self.ur) xx,yy = x - xs, y - ys xp,yp = (xe - xs) / w, (ye - ys) / h xn,yn = xx / xp, h - yy / yp #print x,y,'=>',xn,yn return int(xn), int(yn) class MyFrame(wx.Frame): def __init__(self): # init controls super(MyFrame, self).__init__( None, title='Bezier Curve', style= wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX ) self.panel = wx.Panel(self, size=(400, 400)) self.panel.Bind(wx.EVT_PAINT, self.paint) self.Fit() # init bezier control points self.ctrls = [(2, 7), (6, 14), (10, 12), (13, 2)] def paint(self, e): dc = wx.PaintDC(self.panel) m = Mapper(self.panel.GetSize(), (-1, -1), (15, 15)) self.clear(dc) self.drawgrid(m, dc) self.drawguide(m, dc) self.drawbezier(m, dc) def clear(self, dc): self.setColor(dc,'white') dc.DrawRectangle(*(0, 0) + tuple(self.panel.GetSize())) def drawbezier(self, m, dc): # draw bezier curve self.setColor(dc,'black') #dc.DrawPointList([m.toClient(x, y) for x, y in Bezier(self.ctrls)]) lst = list(Bezier(self.ctrls)) dc.DrawLineList([m.toClient(*p) + m.toClient(*q) for p,q in zip(lst, lst[1:])]) def drawguide(self, m, dc): # self.setColor(dc,'yellow') for p1,p2 in zip(self.ctrls, self.ctrls[1:]): line = m.toClient(*p1) + m.toClient(*p2) dc.DrawLine(*line) self.setColor(dc,'green') lst = [] for p1,p2,p3 in zip(self.ctrls, self.ctrls[1:], self.ctrls[2:]): p = m.toClient(*mid(p1, p2)) q = m.toClient(*mid(p2, p3)) dc.DrawCircle(*p + (2,)) dc.DrawCircle(*q + (2,)) dc.DrawLine(*p + q) lst.append(mid(p, q)) self.setColor(dc,'blue') dc.DrawCircle(*lst[0] + [2,]) dc.DrawCircle(*lst[1] + [2,]) dc.DrawLine(*lst[0] + lst[1]) # draw control points self.setColor(dc,'red') for x,y in self.ctrls: dc.DrawCircle(*m.toClient(x, y) + (3,)) def drawgrid(self, m, dc): xs,ys = m.ll xe,ye = m.ur self.setColor(dc,'black') dc.DrawLine(*m.toClient(xs, 0) + m.toClient(xe, 0)) dc.DrawLine(*m.toClient(0, ys) + m.toClient(0, ye)) dc.SetFont(self.panel.GetFont()) dc.DrawText('%+d' % xs, *m.toClient(xs , 0)) dc.DrawText('%+d' % ye, *m.toClient(0, ye)) #x,y = m.toClient(xe, 0) #dc.DrawText('%+d' % xe, *(x - dc.GetTextExtent('%+d' % xe)[0], y)) #x,y = m.toClient(0, ys) #dc.DrawText('%+d' % ys, *(x, y dc.GetTextExtent('%+d' % ys)[1])) #... def setColor(self, dc, color): dc.SetPen(wx.Pen(color)) dc.SetBrush(wx.Brush(color)) app = wx.PySimpleApp() frame = MyFrame().Show() app.MainLoop()