ベジェ曲線と直線の交点

2019/01/04

Python2.7.8

3次方程式の解を使っての3次ベジェ曲線と直線の交点の算出
(線分の判定はしていない)

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

def intersection_of_bezier_and_line((p0,p1,p2,p3),(a,b,c)):
    f0 =      a * p0[0] + b * p0[1] + c
    f1 = 3 * (a * p1[0] + b * p1[1] + c)
    f2 = 3 * (a * p2[0] + b * p2[1] + c)
    f3 =      a * p3[0] + b * p3[1] + c

    _a = -f0 + f1 - f2 + f3
    _b = 3 * f0 - 2 * f1 + f2
    _c = -3 * f0 + f1
    _d = f0
    if _a == 0:
        if _b == 0:
            if _c == 0: tlist = []
            else: tlist = -_d/_c
        else:
            tlist = quadratic_equation(_b,_c,_d)
    else:
        tlist = cubic_equation(_a,_b,_c,_d)
    points = []
    for t in tlist:
        if t < 0 or 1 < t:
            continue
        x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
        y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
        points.append((x,y))
    return points

def line_generalization(p0,p1):
    if p0[0] == p1[0]:
        a = 1.0
        b = 0.0
        c = float(-p0[0])
    elif p0[1] == p1[1]:
        a = 0.0
        b = 1.0
        c = float(-p0[1])
    else:
        slope = float(p1[1]-p0[1])/(p1[0]-p0[0])
        intercept = p0[1] - slope*p0[0]
        a = slope
        b = -1.0
        c = intercept
    return a,b,c

def cubic_equation(a,b,c,d):
    p = -b**2/(9.0*a**2) + c/(3.0*a)
    q = b**3/(27.0*a**3) - b*c/(6.0*a**2) + d/(2.0*a)
    t = complex(q**2+p**3)

    w =(-1.0 +1j*3.0**0.5)/2.0
    u = [0,0,0]
    u[0] = (-q +t**0.5)**(1.0/3.0)
    u[1] = u[0] * w
    u[2] = u[0] * w**2
    v = [0,0,0]
    v[0] = (-q -t**0.5)**(1.0/3.0)
    v[1] = v[0] * w
    v[2] = v[0] * w**2

    x_list = []
    for i in range(3):
      for j in range(3):
        if abs(u[i]*v[j] + p) < 0.0001:
            x = u[i] + v[j]
            if abs(x.imag) < 0.0000001:
                x = x.real - b/(3.0*a)
                x_list.append(x)

    return x_list

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()

    bezier_points = [[10,10],[100,180],[200,10],[290,190]]
    canvas.create_line(bezier_curve(*bezier_points))

    line_points = [[0,20],[300,180]]
    line_eq = line_generalization(*line_points)
    canvas.create_line(line_points)

    xy = intersection_of_bezier_and_line(bezier_points,line_eq)
    r = 3
    for x,y in xy:
        canvas.create_oval(x-r,y-r,x+r,y+r)    

    window.mainloop()

if __name__ == '__main__':
    main()

5(その2):直線と曲線の交点(解の公式)
https://pgcity.jp/2010/09/05/175/