ベジェ曲線の最大/最小値

2019/01/14

Python2.7.8

3次ベジェ曲線の最大・最小値の取得

# -*- coding: utf-8 -*-

def bezier_box(p0,p1,p2,p3):
    bounds = [[],[]]
    bounds[0] += [p0[0],p3[0]]
    bounds[1] += [p0[1],p3[1]]

    for i in [0,1]:
        a = float(-3 * p0[i] +  9 * p1[i] - 9 * p2[i] + 3 * p3[i])
        b = float( 6 * p0[i] - 12 * p1[i] + 6 * p2[i])
        c = float( 3 * p1[i] -  3 * p0[i])
        if a == 0:
            if b == 0: continue
            tlist = [-c / b]
        else:
            tlist = quadratic_equation(a,b,c)
        for t in tlist:
            if 0 < t < 1: 
                v = (1-t)**3*p0[i] + 3*(1-t)**2*t*p1[i] + 3*(1-t)*t**2*p2[i] + t**3*p3[i]
                bounds[i].append(v)

    return [min(bounds[0]),min(bounds[1]),
            max(bounds[0]),max(bounds[1])]

def quadratic_equation(a,b,c):
    d = b*b-4*a*c
    if d < 0:
        return []
    if d == 0:
        x = -b/(2.0*a)
        return [x]
    else:
        x0 = (-b+d**0.5)/(2.0*a)
        x1 = (-b-d**0.5)/(2.0*a)
    return [x0,x1]

def bezier_curve(p0,p1,p2,p3,n=20):
    p0_x,p0_y = p0
    p1_x,p1_y = p1
    p2_x,p2_y = p2
    p3_x,p3_y = p3
    points = []
    for i in range(n+1):
        t = i/float(n)
        p_x = (1-t)**3*p0_x + 3*(1-t)**2*t*p1_x + 3*(1-t)*t**2*p2_x + t**3*p3_x
        p_y = (1-t)**3*p0_y + 3*(1-t)**2*t*p1_y + 3*(1-t)*t**2*p2_y + t**3*p3_y
        points.append([p_x,p_y])
    return points

def main():
    import Tkinter
    window = Tkinter.Tk()
    canvas = Tkinter.Canvas(window,width=300,height=200)
    canvas.pack() 

    points = [[20,60],[120,10],[290,190],[220,140]]
    box = bezier_box(*points)

    r = 3
    for p in [points]:
        for i in range(4):
            canvas.create_oval(p[i][0]-r,p[i][1]-r,
                               p[i][0]+r,p[i][1]+r)
        canvas.create_line(p[0][0],p[0][1],p[1][0],p[1][1])
        canvas.create_line(p[2][0],p[2][1],p[3][0],p[3][1])

    canvas.create_line(bezier_curve(*points))
    canvas.create_line(box[0],box[1],box[0],box[3],fill='red')
    canvas.create_line(box[2],box[1],box[2],box[3],fill='red')
    canvas.create_line(box[0],box[1],box[2],box[1],fill='red')
    canvas.create_line(box[0],box[3],box[2],box[3],fill='red')

    window.mainloop()
    return

if __name__ == '__main__':
    main()


ベジエ曲線のバウンディングボックス
http://d.hatena.ne.jp/nishiohirokazu/20090616/1245104751