[Python]Spline(곡선)_Parametric Curves - Hermite, Bezier

Spline

참고로 자세한 개념들은 생략

  • 곡선을 표현하기 위해 사용하던 얇은 막대
  • 무거운 추의 무게에 의해 막대가 휘어짐

    • 필요한 곡률에 맞춰 세밀한 조정이 가능
  • CAD

    • 컴퓨터로 설계하는 모델링 프로그램
    • 수학식을 이용하여 부드러운 곡선 표현
    • 무거운 추 = Control Points(조절점)


Control Points(조절점)

  • Interpolating : 모든 CP를 통과
  • Approximating : CP를 근접 / 처음, 마지막 CP 통과


Parametric Curve

  • 매개변수 방정식을 사용하여 정의한 곡선

  • Hermite Spline - Interpolating

  • Bezier Spline - Approximating

import numpy as np
import matplotlib.pyplot as plt
import scipy as sp

Circle

t = np.linspace(0,2*np.pi, 1000) # piecewise t
x = np.cos(t)
y = np.sin(t)
fig = plt.figure()
ax = fig.add_subplot(111) # 1x1 의 1번째 subplot
ax.set_aspect('equal', adjustable='box')
plt.plot(x,y)
plt.show()

Lissajous

t = np.linspace(0,2*np.pi,1000)
x = np.cos(13*t)
y = np.sin(11*t)
fig = plt.figure()
ax = fig.add_subplot(111) # 1x1 의 1번째 subplot
ax.set_aspect('equal', adjustable='box')
plt.plot(x,y)
plt.show()

Parametric Curve

t = np.linspace(-2,2,1000)
x = t**2 + t
y = t**2 - t
plt.plot(x,y)
plt.show()

Hermite Spline 실습

image-20220901013158026

  • 두 점(p0, p1)의 위치와 접선 벡터(v0, v1)을 이용해 곡선을 구현

  • 공식은 아래 함수내에 crvPt이다.

def HermiteCurve2D(p0,p1,v0,v1,res): # res : 출력으로 나오는 hermite curve의 점 개수
    xAns=[]
    yAns=[]
    v0 = v0/np.linalg.norm(v0) # 정규화
    v1 = v1/np.linalg.norm(v1)
    for i in range(0, res+1):
        t = i / res # 0...1
        t2 = t*t
        t3 = t*t*t
        h0 = 2*t3-3*t2+1
        h1 = -2*t3+3*t2
        h2 = t3-2*t2+t
        h3 = t3-t2
        crvPt = h0*p0+h1*p1+h2*v0+h3*v1 # [1. 1.] 이런 형태로 출력됨.
        xAns.append(crvPt[0]) # x값
        yAns.append(crvPt[1]) # y값
    return xAns, yAns

# line
p0 = np.array([1,1])
p1 = np.array([4,1])
v0 = np.array([1,0])
v1 = np.array([1,0])

xAns, yAns = HermiteCurve2D(p0,p1,v0,v1,15)
plt.plot(xAns,yAns,'ro-')
plt.arrow(p0[0],p0[1],v0[0],v0[1])
plt.arrow(p1[0],p1[1],v1[0],v1[1])
plt.ylim(0.8,1.3)
(0.8, 1.3)

# arch
p0 = np.array([1,1])
p1 = np.array([4,1])
v0 = np.array([1,1])
v1 = np.array([1,-1])

xAns, yAns = HermiteCurve2D(p0,p1,v0,v1,15)
plt.plot(xAns,yAns,'ro-')
plt.arrow(p0[0],p0[1],v0[0],v0[1])
plt.arrow(p1[0],p1[1],v1[0],v1[1])
plt.ylim(0.8,1.3)
(0.8, 1.3)

Bezier Curve 실습

image-20220901013128661

  • n(n>=2)개의 조절점(Control Point)로 정의되는 매개변수 곡선
  • n개의 점으로 (n-1)차 베지에 곡선 생성 가능
  • n차 베지에 곡선은 (n-1)차 베지에 곡선의 선형보간으로 표현
  • 공식은 tmp 부분이다
  • 애니메이션 참고하기
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import factorial
def BernsteinPolynomials(i,n,t):
    a=factorial(n)
    b=factorial(i)*factorial(n-i)
    c=(1-t)**(n-i)
    d=t**i
    return a*c*d/b

def BezierCurve(ctrlPts,t):
    ans = np.array([0.0, 0.0]) # [0,0] X
    n = len(ctrlPts)-1
    for i in range(0,n+1):
        tmp = (ctrlPts[i]*BernsteinPolynomials(i,n,t)) # Pi * Bi,n,t (i=0,1,...n)
        ans[0] += tmp[0] # 예로 x좌표
        ans[1] += tmp[1] # y좌표로 봐도 될듯.
    return ans
p0, p1, p2, p3 = [0,0], [2,5], [4,5], [6,0] # 각 P의 위치 값
ctrlPts = np.array([p0,p1,p2,p3]) # 4개의 P(포인터)
bezPtsX = [] # matplotlib에서 그림을 그리기 위해..
bezPtsY = []
ctrlPtsX = [] # 베지어 커브 적용하기 전 모습 그리기 위함.
ctrlPtsY = []

for pt in ctrlPts:
    ctrlPtsX.append(pt[0])
    ctrlPtsY.append(pt[1])

sampleNum = 10 # bez cv pts 나누는 수 t 0, 0.1, 0.2, ... , 1.0
for i in range(0, sampleNum+1):
    t = i/sampleNum
    bezPts = BezierCurve(ctrlPts, t)
    bezPtsX.append(bezPts[0])
    bezPtsY.append(bezPts[1])

plt.plot(bezPtsX, bezPtsY, 'ro-')
plt.plot(ctrlPtsX, ctrlPtsY, 'bo-')
plt.show()

p0, p1, p2, p3 = [0,0], [4,5], [2,5], [6,0] # 각 P의 위치 값
ctrlPts = np.array([p0,p1,p2,p3]) # 4개의 P(포인터)
bezPtsX = [] # matplotlib에서 그림을 그리기 위해..
bezPtsY = []
ctrlPtsX = [] # 베지어 커브 적용하기 전 모습 그리기 위함.
ctrlPtsY = []

for pt in ctrlPts:
    ctrlPtsX.append(pt[0])
    ctrlPtsY.append(pt[1])

sampleNum = 10 # bez cv pts 나누는 수 t 0, 0.1, 0.2, ... , 1.0
for i in range(0, sampleNum+1):
    t = i/sampleNum
    bezPts = BezierCurve(ctrlPts, t)
    bezPtsX.append(bezPts[0])
    bezPtsY.append(bezPts[1])

plt.plot(bezPtsX, bezPtsY, 'ro-')
plt.plot(ctrlPtsX, ctrlPtsY, 'bo-')
plt.show()

추가로 구해본것(그래프 4개로 표현)

count = 0
fig, axss = plt.subplots(2, 2, figsize=(15, 10)) # 2x2 인 plot들을 생성(4개 그래프 생성)
fig.suptitle('Graph Total') # 전체 제목
while(True):
    if count == 0:
        p0, p1, p2, p3 = [0.5,0], [0.5,0.27614237502713], [0.27614237502713,0.5], [0,0.5]
        axs = axss[0,0] # 첫번째 그래프
        axs.set_title('first graph')
    elif count == 1:
        p0, p1, p2, p3 = [0,0.5] , [-0.27614237502713,0.5], [-0.5,0.27614237502713], [-0.5,0]
        axs = axss[0,1] # 두번째
        axs.set_title('second graph')
    elif count == 2:
        p0, p1, p2, p3 = [-0.5,0] , [-0.5,-0.27614237502713], [-0.27614237502713,-0.5], [0,-0.5]
        axs = axss[1,0] # 세번째
        axs.set_title('third graph')
    elif count == 3:
        p0, p1, p2, p3 = [0,-0.5] , [0.27614237502713,-0.5], [0.5,-0.27614237502713], [0.5,0]
        axs = axss[1,1] # 네번째
        axs.set_title('forth graph')
    else:
        break
    count += 1
    ctrlPts = np.array([p0,p1,p2,p3]) # 4개의 P(포인터)
    bezPtsX, bezPtsY, ctrlPtsX, ctrlPtsY = [], [], [], []
    for pt in ctrlPts:
        ctrlPtsX.append(pt[0])
        ctrlPtsY.append(pt[1])
    sampleNum = 10 # bez cv pts 나누는 수 t 0, 0.1, 0.2, ... , 1.0
    for i in range(0, sampleNum+1):
        t = i/sampleNum
        bezPts = BezierCurve(ctrlPts, t)
        bezPtsX.append(bezPts[0])
        bezPtsY.append(bezPts[1])
    axs.plot(bezPtsX, bezPtsY, 'ro-')
    axs.plot(ctrlPtsX, ctrlPtsY, 'bo-')
plt.show()

추가로 구해본것(한 그래프로 합침)

count = 0
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_aspect('equal', adjustable='box')
while(True):
    if count == 0:
        p0, p1, p2, p3 = [0.5,0], [0.5,0.27614237502713], [0.27614237502713,0.5], [0,0.5]
    elif count == 1:
        p0, p1, p2, p3 = [0,0.5] , [-0.27614237502713,0.5], [-0.5,0.27614237502713], [-0.5,0]
    elif count == 2:
        p0, p1, p2, p3 = [-0.5,0] , [-0.5,-0.27614237502713], [-0.27614237502713,-0.5], [0,-0.5]
    elif count == 3:
        p0, p1, p2, p3 = [0,-0.5] , [0.27614237502713,-0.5], [0.5,-0.27614237502713], [0.5,0]
    else:
        break
    count += 1
    ctrlPts = np.array([p0,p1,p2,p3]) # 4개의 P(포인터)
    bezPtsX, bezPtsY, ctrlPtsX, ctrlPtsY = [], [], [], []
    for pt in ctrlPts:
        ctrlPtsX.append(pt[0])
        ctrlPtsY.append(pt[1])
    sampleNum = 10 # bez cv pts 나누는 수 t 0, 0.1, 0.2, ... , 1.0
    for i in range(0, sampleNum+1):
        t = i/sampleNum
        bezPts = BezierCurve(ctrlPts, t)
        bezPtsX.append(bezPts[0])
        bezPtsY.append(bezPts[1])
    plt.plot(bezPtsX, bezPtsY, 'ro-')
    plt.plot(ctrlPtsX, ctrlPtsY, 'bo-')
plt.show()

댓글남기기