Processing math: 20%

Spline은 일정한 매개변수 구간 [a,b]에서 분포된 값(다차원인 경우는 벡터)을 이용해서 만든 다항식을 의미한다. 매개변수 구간 [a,b]는 값이 주어진 부분구간들의 합으로 표현할 수 있는데 이 부분구간의 시작과 끝에 해당하는 매개변수 값을 knot이라고 한다. knot이 매개변수 구간 [a,b]에 균일하게 퍼져 있는 경우는 uniform spline, 그렇지 않은 경우는 non-uniform spline이라고 한다.

Catmull-Rom spline은 3차 다항식으로 써지는 uniform spline으로 각 knot 위치에서 주어진 값을 통과하므로 interpolation 함수이기도 한다. 그리고 1차 도함수가 연속성을 가지고 있고, knot의 위치 변화나 knot에서 주어진 값의 변화에 곡선이 민감하게 변하지 않는다. 그러나 2차원 이상에는 Catmull-Rom spline이 꼬이는 현상이 발생할 수 있고, 극단적으로는 cusp가 생기는 단점도 가지고 있다.

2차원 이상에서는 Catmull-Rom spline으로 주어진 점(벡터)들을 보간하는 곡선을 찾을 때 uniform이 아니면 knot을 정하는데 모호함이 존재한다. 보통은 순차적으로 주어진 입력점의 직선거리를 이용해서 knot을 정할 수 있는데 이 경우를 chordal Catmull-Rom spline이라고 한다.

주어진 입력점이 {P0,P1,P2,P3}일 때 knot은

ti=ti1+||Pi

더 일반적으로 

ti=ti1+||PiPi1||α,   α[0,1]

로 잡을 수 있는데 이 경우를 centripetal Catmull-Rom spline이라 하며 cusp를 피할 수 있다. 단, 입력점의 중복이 없도록 미리 제거되어야 한다. Spline S(t)P1P2사이에서

  • 3차 함수로 쓰여져야 하고,
  • 입력점 통과: S(t1)=P1, S(t2)=P2 ,
  • 기울기: S(t1)=1t2t0(P2P0), S(t2)=1t3t1(P3P1)

인 조건을 사용하면 S(t)에 대한 closed form을 얻을 수 있다(참고: 위키피디아). S(t)t1tt2 사이에서 정의되고, 곡선을 그릴 때 매개변수를 t[0:1] 으로 잡으면 S(t)를 호출할 때는 t1+(t2t1)t를 인자로 사용하면 된다.

ref: http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf

https://kipl.tistory.com/226

 

Catmull-Rom Spline

두 개의 점이 주어지는 경우 이들을 잇는 가장 단순한 곡선은 직선이다. 또 직선은 두 점을 잇는 가장 짧은 거리를 갖는 곡선이기도 하다. 그러면 N개의 점이 주어지는 경우는 이들 모두 지나는

kipl.tistory.com

 

red=centripetal Catmull-Rom Spline

double GetParam(double t, double alpha, const CfPt& p0, const CfPt& p1 ) {
    CfPt p = p1 - p0;
    double dsq = p.x * p.x + p.y * p.y;
	if (dsq == 0) return t;
    return pow(a, alpha / 2) + t;
}
//centripetal catmull-rom spline;
CfPt CRSplineA(double t /*[0:1]*/, 
               const CfPt& p0, 
               const CfPt& p1, 
               const CfPt& p2, 
               const CfPt& p3, double alpha=.5 /*[0:1]*/)
{
    double t0 = 0;
    double t1 = GetParam( t0, alpha, p0, p1 );
    double t2 = GetParam( t1, alpha, p1, p2 );
    double t3 = GetParam( t2, alpha, p2, p3 );
    t = t1 * (1 - t) + t2 * t;
    CfPt A1 = t1==t0 ? p0: p0*(t1-t)/(t1-t0) + p1*(t-t0)/(t1-t0);
    CfPt A2 = t2==t1 ? p1: p1*(t2-t)/(t2-t1) + p2*(t-t1)/(t2-t1);
    CfPt A3 = t3==t2 ? p2: p2*(t3-t)/(t3-t2) + p3*(t-t2)/(t3-t2);
    CfPt B1 = A1 * (t2-t)/(t2-t0) + A2 * (t-t0)/(t2-t0);
    CfPt B2 = A2 * (t3-t)/(t3-t1) + A3 * (t-t1)/(t3-t1);
    return B1 * (t2-t)/(t2-t1) + B2 * (t-t1)/(t2-t1);
}
void DrawCatmullRom(std::vector<CfPt>& Q, CDC *pDC) {  
#define STEPS (20)
    if (Q.size() < 4) return ;
    CPen red(PS_SOLID, 2, RGB(255, 0, 0));
    CPen *pOld = pDC->SelectObject(&red);
    const int n = Q.size();
    // centripetal Catmull-Rom spline;
    for (int i = 0; i < n - 1; i++) {
        pDC->MoveTo(Q[i]);
        // i = 0 인 경우에는 처음 점을 반복, i = n - 2인 경우에는 마지막 점을 반복..
        int ip = max(i - 1, 0);
        int inn = min(i + 2, n - 1);
        for (int it = 1; it <= STEPS; it++)
            pDC->LineTo(CRSplineA(double(it)/STEPS, Q[ip], Q[i], Q[i + 1], Q[inn]));
    };
    pDC->SelectObject(pOld);
    CPen blue(PS_SOLID, 2, RGB(0, 0, 255));
    pOld = pDC->SelectObject(&blue);
    // uniform Catmull-Rom spline;
    for (int i = 0; i < n - 1; i++) {
        pDC->MoveTo(Q[i]);
        // i = 0 인 경우에는 처음 점을 반복, i = n - 2인 경우에는 마지막 점을 반복..
        int ip = max(i - 1, 0);
        int inn = min(i + 2, n - 1);
        for (int it = 1; it <= STEPS; it++)
            pDC->LineTo(CRSpline(double(it)/STEPS, Q[ip], Q[i], Q[i + 1], Q[inn]));
    };
    pDC->SelectObject(pOld);
}
728x90

'Computational Geometry' 카테고리의 다른 글

Natural Cubic Spline: revisited  (0) 2024.06.14
Polygon Decimation  (0) 2024.06.08
Bezier curves  (1) 2024.04.19
Subdividing a Bezier Curve  (0) 2024.04.18
Derivatives of Bezier Curves  (1) 2024.04.12
,