입력점들을 부드럽게 연결하는 베지어 곡선을 찾아보자. 입력점 사이 구간을 3차 베지어 곡선으로 표현하려면 4개의 컨트롤점이 필요한데, 최소자승법을 이용하지 않고 순차적인 3 입력점 $P_0, P_1, P_2$이 주어질 때 이를 이용해서 컨트롤 점을 구성한다.(따라서 베지어 곡선은 시작과 끝이 아닌 입력점을 일반적으로 통과하지 않는다) $$C_0 = (P_0+P_1)/2,~~C_1 = (P_0 +5 P_1)/6,~~C_2 = (5P_1+ P_2)/6,~~C_3=(P_1+P_2)/2$$ 컨트롤점으로 하는 베지어 곡선은 $[P_0, P_1, P_2]$의 일부분을 표현한다. 이 과정은 다음 한 입력점을 포함시켜서 다시 반복을 하는데 이 떄 이전 베이어 곡선의 마지막 컨트롤점($C_2$)이 새로이 만들어지는 베지어 곡선의 시작점($C_0^\text{new}$)이 되므로 두 베지어 곡선은 부드럽게 연결이 된다. 입력점의 시작과 끝점은 베이어 곡선이 통과하도록 컨트롤점에 포함되게 설계한다.
$$\text{front}:~ C_0=P_0, ~~C_1 = (P_0+2P_1)/3$$
$$\text{back}:~C_2=(2P_1+P_2)/3, ~~C_3=P_3$$
그리고 주어진 베지어 곡선의 분할은 균일한 간격으로 하지 않고, 각각의 구간에서 베지어 곡선이 충분히 평탄하도록 분할해서 저장한다.
// test flatness of a bezier curve with control points {p1, c1, c2, p2};
bool FlatBezier(const double eps,
const CfPt& p1, const CfPt& c1, const CfPt& c2, const CfPt& p2) {
CfPt U = c1 - (p1 * 2 + p2) / 3;
CfPt V = c2 - (p1 + p2 * 2) / 3;
double normU = hypot(U.x, U.y);
double normV = hypot(V.x, V.y);
return (3. * max(normU, normV) / 4.) <= eps;
}
// subdivision stage; De Casteljau's algorithm
void GetBezierPoints(const double eps,
const CfPt &p1, const CfPt &c1, const CfPt &c2, const CfPt &p2,
std::vector<CfPt>& smoothed) {
if (!FlatBezier(eps, p1, c1, c2, p2)) {
// Subdivide the curve at t = 0.5
// left;
CfPt mid_segm = (p1 + c1 * 3 + c2 * 3 + p2) / 8.0; // = B(1/2);
CfPt new_c1 = (p1 + c1) / 2.0;
CfPt new_c2 = (p1 + c1 * 2 + c2) / 4.0; // ((p1+c1)/2 + (c1+c2)/2)/2
GetBezierPoints(eps, p1, new_c1, new_c2, mid_segm, smoothed);
// right;
new_c1 = (c1 + c2 * 2 + p2) / 4.0;
new_c2 = (c2 + p2) / 2.0;
GetBezierPoints(eps, mid_segm, new_c1, new_c2, p2, smoothed);
}
else smoothed.push_back(p2); // flat enough;
}
// linear interpolation between a and b;
// a와 b 사이를 (1-t): t로 내분;
CfPt lerp(const double t, const CfPt &a, const CfPt &b) {
return a * (1 - t) + b * t;
}
std::vector<CfPt> SmoothPathWithBezier(const std::vector<CfPt>& points) {
if (points.size() < 3) return std::vector<CfPt> ();//null vector;
CfPt cntl[4];
CfPt *pts = &points[0]; // iterator;
const int N = points.size();
std::vector<CfPt> smoothed;
smoothed.push_back(pts[0]);
for (int i = 2; i < N; i++, pts++) {
if (i == 2) {
cntl[0] = pts[0];
cntl[1] = lerp(2./ 3, pts[0], pts[1]);
} else {
//0번 control-pts= (0-1)의 중간점;
//1번 control-pts= (0-1)의 5:1 내분점;
cntl[0] = lerp(1./ 2, pts[0], pts[1]);
cntl[1] = lerp(5./ 6, pts[0], pts[1]);
}
if (i == N-1) {
//1:2 내분점;
cntl[2] = lerp(1./ 3, pts[1], pts[2]);
cntl[3] = pts[2];
} else {
//2번 control-pts;(1-2)의 1:5 내분점;
//3번 control-pts;(1-2)의 중간점;
cntl[2] = lerp(1./ 6, pts[1], pts[2]);
cntl[3] = lerp(1./ 2, pts[1], pts[2]);
}
//입력 중에 처음 두개가 같거나, 다음 두개가 같으면 마지막이 충분히 smooth하므로
// 마직막 control pt만 베지어 곡선에 삽입;
if ((pts[0] == pts[1]) || (pts[1] == pts[2]))
smoothed.push_back(cntl[3]);
else {
double tolerance = 1.; // 1-pixel;
GetBezierPoints(tolerance, cntl[0], cntl[1], cntl[2], cntl[3], smoothed);
}
}
return smoothed;
}
728x90
'Computational Geometry' 카테고리의 다른 글
DDA Algorithm (0) | 2021.04.25 |
---|---|
B-Spline (0) | 2021.04.25 |
Flatness of Cubic Bezier Curve (0) | 2021.04.23 |
Convexity of Bezier Curve (0) | 2021.04.22 |
De Casteljau's Algorithm (0) | 2021.04.22 |