[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 실습
-
두 점(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 실습
- 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()
댓글남기기