Hermite曲线与Bezier曲线的关系

    技术2025-02-02  8

    结论

    最近在研究3次样条曲线。曲线由四个控制点控制,依次记为P0,P1,P2,P3。在绘制Hermite曲线的时候,发现如果令P0处的导数为3倍P1-P0,P3处的导数为3倍P3-P2,则P0,P1,P2,P3构成的Hermite曲线与P0,P1,P2,P3构成的Bezier曲线完全相同。

    下面详细分析说明这一点

    分析

    通用的三次Hermite曲线的参数表达式为

    P ( u ) = P 0 ( 2 u 3 − 3 u 2 + 1 ) + P 3 ( − 2 u 3 + 3 u 2 ) + P 0 ′ ( u 3 − 2 u 2 + u ) + P 3 ′ ( u 3 − u 2 ) P(u)=P_0(2u^3-3u^2+1)+P_3(-2u^3+3u^2)+P'_0(u^3-2u^2+u)+P'_3(u^3-u^2) P(u)=P0(2u33u2+1)+P3(2u3+3u2)+P0(u32u2+u)+P3(u3u2)

    刚刚我们说明了,如果 P 0 ′ = 3 ( P 1 − P 0 ) P'_0=3(P_1-P_0) P0=3(P1P0), P 3 ′ = 3 ( P 3 − P 2 ) P'_3=3(P_3-P_2) P3=3(P3P2),那么上式又可以化为 P ( u ) = ( u 3 − 3 u 2 + 1 ) P 0 + ( − 2 u 3 + 3 u 2 ) P 3 + 3 ( P 1 − P 0 ) ( u 3 − 2 u 2 + u ) + 3 ( P 3 − P 2 ) ( u 3 − u 2 ) P(u)=(u^3-3u^2+1)P_0+(-2u^3+3u^2)P_3+3(P_1-P_0)(u^3-2u^2+u)+3(P_3-P_2)(u^3-u^2) P(u)=(u33u2+1)P0+(2u3+3u2)P3+3(P1P0)(u32u2+u)+3(P3P2)(u3u2) 整理一下可以得到 P ( u ) = ( 1 − 3 u + 3 u 2 − u 3 ) P 0 + ( 3 u − 6 u 2 + 3 u 3 ) P 1 + ( 3 u 2 − 3 u 3 ) P 2 + u 3 P 3 P(u)=(1-3u+3u^2-u^3)P_0+ (3u-6u^2+3u^3)P_1+(3u^2-3u^3)P_2+u^3P_3 P(u)=(13u+3u2u3)P0+(3u6u2+3u3)P1+(3u23u3)P2+u3P3 P ( u ) = ( 1 − u ) 3 P 0 + 3 u ( 1 − u ) 2 P 1 + 3 u 2 ( 1 − u ) P 2 + u 3 P 3 P(u)=(1-u)^3P_0+3u(1-u)^2P_1+3u^2(1-u)P_2+u^3P_3 P(u)=(1u)3P0+3u(1u)2P1+3u2(1u)P2+u3P3

    这正是三次Bezier曲线的公式

    Demo演示

    下面用OpenCV分别画Hermite曲线和Bezier曲线,两条曲线完全重合。

    Demo演示代码

    #include <opencv2/opencv.hpp> #include <iostream> #include <vector> #include <cmath> using namespace std; using namespace cv; string mainwindow = "构造线"; Point2d control_points[4]; int control_points_index = 0; int height = 600; int width = 800; Mat screen_mat; const double CHOOSE_THRESHOLD = 20; bool choose_flag = false; int choose_point; const int LINE_N = 1000; // 线段的个数,点数为LINE_N+1 void on_mouse_handler(int event, int x, int y, int flag, void* param) /* x, y 的原点在左上角 */ { double dist; if (event == EVENT_LBUTTONDOWN) { if (control_points_index < 4) { control_points[control_points_index].x = x; control_points[control_points_index].y = y; control_points_index++; } else { for (int i = 0; i < 4; i++) { dist = (control_points[i].x - x) * (control_points[i].x - x) + (control_points[i].y - y) * (control_points[i].y - y); if (dist < CHOOSE_THRESHOLD * CHOOSE_THRESHOLD) { //cout << "Choose " << i << endl; choose_flag = true; choose_point = i; return; } } } } else if (event == EVENT_LBUTTONUP) { choose_flag = false; } else if (event == EVENT_MOUSEMOVE) { if (choose_flag) { control_points[choose_point].x = x; control_points[choose_point].y = y; } } } void imag_update() { screen_mat = Mat::zeros(height, width, CV_8UC4); for (int i = 0; i < control_points_index; i++) { drawMarker(screen_mat, control_points[i], Scalar(0, 0, 255, 255), MARKER_STAR, 5); } if (control_points_index == 4) { Point2d v0, v1, v2, v3; Point2d bv0, bv1, bv2, bv3; //Hermite v0 = control_points[0]; v1 = control_points[3]; v2 = control_points[1] - control_points[0]; v3 = control_points[3] - control_points[2]; v2 = 3 * v2; v3 = 3 * v3; //Bezier bv0 = control_points[0]; bv1 = control_points[1]; bv2 = control_points[2]; bv3 = control_points[3]; Point2d p0 = control_points[0]; Point2d bp0 = control_points[0]; Point2d p1; Point2d bp1; double u; double c0, c1, c2, c3; double bc0, bc1, bc2, bc3; for (int i = 1; i < LINE_N; i++) { u = i * 1.0 / LINE_N; c0 = 2 * u * u * u - 3 * u * u + 1; c1 = -2 * u * u * u + 3 * u * u; c2 = u * u * u - 2 * u * u + u; c3 = u * u * u - u * u; //Bezier bc0 = (1 - u) * (1 - u) * (1 - u); bc1 = 3 * u * (1 - u) * (1 - u); bc2 = 3 * u * u * (1 - u); bc3 = u * u * u; p1.x = v0.x * c0 + v1.x * c1 + v2.x * c2 + v3.x * c3; p1.y = v0.y * c0 + v1.y * c1 + v2.y * c2 + v3.y * c3; bp1.x = bv0.x * bc0 + bv1.x * bc1 + bv2.x * bc2 + bv3.x * bc3; bp1.y = bv0.y * bc0 + bv1.y * bc1 + bv2.y * bc2 + bv3.y * bc3; line(screen_mat, p0, p1, Scalar(0, 0, 255, 128), 1, LINE_AA); line(screen_mat, bp0, bp1, Scalar(0, 255, 0, 128), 1, LINE_AA); p0 = p1; bp0 = bp1; } } imshow(mainwindow, screen_mat); } void window_initialize() { namedWindow(mainwindow, WINDOW_AUTOSIZE); setMouseCallback(mainwindow, on_mouse_handler); } void main_loop() { /* Endless loop until ESC key pressed*/ bool finished = false; int pressed_key = 0; while (!finished) { imag_update(); pressed_key = waitKey(10); if (pressed_key == 27) finished = true; } } int main() { window_initialize(); main_loop(); return 0; }
    Processed: 0.011, SQL: 9