C语言如何实现DrawBezier绘制贝塞尔曲线?

99ANYc3cd6
预计阅读时长 33 分钟
位置: 首页 C语言 正文

下面我将为你介绍三种主流的方法,从最推荐到最基础的,并附上完整的代码示例。

使用 graphics.h 库 (简单易学,适合初学者)

graphics.h 是一个古老的、非标准的图形库,常见于早期的Turbo C/C++编译器(如 conio.hdos.h 一样),虽然它已经过时,但对于学习基本图形绘制概念非常直观,很多学校的C语言课程仍在使用它。

特点:

  • 优点:API简单,代码量少,容易上手。
  • 缺点:平台兼容性差,只能在Windows的特定环境下编译(如 BGI 库),不适合现代软件开发。

示例代码:绘制三次贝塞尔曲线

#include <graphics.h>
#include <conio.h> // 用于 _getch()
int main() {
    // 初始化图形窗口,"C:\\TC\\BGI" 是你的BGI库路径,请根据实际情况修改
    int gd = DETECT, gm;
    initgraph(&gd, &gm, "C:\\TC\\BGI");
    // 定义三次贝塞尔曲线的四个控制点
    // (x1, y1) 起始点
    // (x2, y2) 控制点1
    // (x3, y3) 控制点2
    // (x4, y4) 结束点
    int x1 = 50, y1 = 200;
    int x2 = 150, y2 = 50;
    int x3 = 250, y3 = 50;
    int x4 = 350, y4 = 200;
    // 绘制控制点(用小圆圈表示)
    setcolor(YELLOW);
    circle(x1, y1, 5);
    circle(x2, y2, 5);
    circle(x3, y3, 5);
    circle(x4, y4, 5);
    // 绘制控制点之间的辅助线(虚线)
    setcolor(WHITE);
    setlinestyle(DOTTED_LINE, 0, 1);
    line(x1, y1, x2, y2);
    line(x2, y2, x3, y3);
    line(x3, y3, x4, y4);
    // 绘制实线样式的贝塞尔曲线
    setcolor(GREEN);
    setlinestyle(SOLID_LINE, 0, 3); // 设置线宽为3
    drawbezier(x1, y1, x2, y2, x3, y3, x4, y4);
    // 在窗口显示文字
    outtextxy(150, 250, "Bezier Curve Example");
    // 按任意键关闭图形窗口
    _getch();
    closegraph();
    return 0;
}

如何编译和运行 (以 Dev-C++ 为例):

  1. 下载 graphics.hwinbgim.h 库文件,并将它们放到你的编译器 include 目录下。
  2. 下载 libbgi.a 库文件,并将其放到你的编译器 lib 目录下。
  3. 创建一个新的项目,将上述代码粘贴进去。
  4. 编译并运行。

使用 Win32 API (Windows平台原生,功能强大)

如果你是在Windows平台上进行开发,使用Win32 API是更现代、更强大的选择,它不依赖于任何第三方库(除了Windows SDK本身)。

特点:

  • 优点:Windows平台原生支持,性能高,功能极其丰富,是Windows桌面程序开发的基础。
  • 缺点:API相对复杂,代码量比 graphics.h 多,需要理解Windows的消息机制。

示例代码:在窗口中绘制贝塞尔曲线

#include <windows.h>
// 窗口过程的函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 窗口类名
    const char* CLASS_NAME = "BezierWindowClass";
    // 注册窗口类
    WNDCLASS wc = { };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClass(&wc);
    // 创建窗口
    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        "Win32 Bezier Curve Example",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style
        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL,       // Parent window
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );
    if (hwnd == NULL) {
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    // 消息循环
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            // 定义贝塞尔曲线的四个控制点
            POINT pts[4] = {
                {50, 200},   // 起始点
                {150, 50},   // 控制点1
                {250, 50},   // 控制点2
                {350, 200}   // 结束点
            };
            // 绘制控制点
            HPEN hPenYellow = CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
            HPEN hOldPen = (HPEN)SelectObject(hdc, hPenYellow);
            for (int i = 0; i < 4; i++) {
                MoveToEx(hdc, pts[i].x - 5, pts[i].y, NULL);
                LineTo(hdc, pts[i].x + 5, pts[i].y);
                MoveToEx(hdc, pts[i].x, pts[i].y - 5, NULL);
                LineTo(hdc, pts[i].x, pts[i].y + 5);
            }
            SelectObject(hdc, hOldPen);
            DeleteObject(hPenYellow);
            // 绘制辅助线
            HPEN hPenDotted = CreatePen(PS_DOT, 1, RGB(200, 200, 200));
            hOldPen = (HPEN)SelectObject(hdc, hPenDotted);
            Polyline(hdc, pts, 4);
            SelectObject(hdc, hOldPen);
            DeleteObject(hPenDotted);
            // 绘制贝塞尔曲线
            HPEN hPenGreen = CreatePen(PS_SOLID, 3, RGB(0, 200, 0));
            hOldPen = (HPEN)SelectObject(hdc, hPenGreen);
            PolyBezier(hdc, pts, 4);
            SelectObject(hdc, hOldPen);
            DeleteObject(hPenGreen);
            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

如何编译和运行:

  1. 将代码保存为 .c 文件(bezier_win32.c)。
  2. 使用支持Windows SDK的编译器(如Visual Studio的 cl.exe 或MinGW的 gcc)进行编译。
    • 使用Visual Studio: 创建一个 "Windows 桌面应用程序" 项目,将代码粘贴进去即可。
    • 使用MinGW/GCC: 在命令行运行 gcc bezier_win32.c -o bezier_win32.exe -mwindows,然后运行生成的 .exe 文件。

手动实现贝塞尔曲线算法 (最底层,适合理解原理)

如果你想真正理解贝塞尔曲线是如何工作的,可以自己动手实现它的绘制算法,贝塞尔曲线的核心是德卡斯特里奥算法

我们将使用Windows GDI的 SetPixel 函数来逐个像素点绘制。

示例代码:手动实现并绘制

#include <windows.h>
#include <math.h>
// 窗口过程
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// 使用德卡斯特里奥算法计算t时刻的贝塞尔曲线上的点
POINT CalculateBezierPoint(float t, POINT* controlPoints, int numPoints) {
    POINT* tempPoints = (POINT*)malloc(numPoints * sizeof(POINT));
    for (int i = 0; i < numPoints; i++) {
        tempPoints[i] = controlPoints[i];
    }
    for (int r = 1; r < numPoints; r++) {
        for (int i = 0; i < numPoints - r; i++) {
            tempPoints[i].x = (1 - t) * tempPoints[i].x + t * tempPoints[i + 1].x;
            tempPoints[i].y = (1 - t) * tempPoints[i].y + t * tempPoints[i + 1].y;
        }
    }
    POINT result = tempPoints[0];
    free(tempPoints);
    return result;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    const char* CLASS_NAME = "ManualBezierClass";
    WNDCLASS wc = { };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    RegisterClass(&wc);
    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "Manual Bezier Curve",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );
    if (hwnd == NULL) return 0;
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            // 定义三次贝塞尔曲线的四个控制点
            POINT controlPoints[4] = {
                {50, 200},
                {150, 50},
                {250, 50},
                {350, 200}
            };
            // 1. 绘制控制点
            SetPixel(hdc, controlPoints[0].x, controlPoints[0].y, RGB(255, 255, 0));
            SetPixel(hdc, controlPoints[1].x, controlPoints[1].y, RGB(255, 255, 0));
            SetPixel(hdc, controlPoints[2].x, controlPoints[2].y, RGB(255, 255, 0));
            SetPixel(hdc, controlPoints[3].x, controlPoints[3].y, RGB(255, 255, 0));
            // 2. 绘制辅助线
            HPEN hPenDotted = CreatePen(PS_DOT, 1, RGB(200, 200, 200));
            HPEN hOldPen = (HPEN)SelectObject(hdc, hPenDotted);
            MoveToEx(hdc, controlPoints[0].x, controlPoints[0].y, NULL);
            LineTo(hdc, controlPoints[1].x, controlPoints[1].y);
            LineTo(hdc, controlPoints[2].x, controlPoints[2].y);
            LineTo(hdc, controlPoints[3].x, controlPoints[3].y);
            SelectObject(hdc, hOldPen);
            DeleteObject(hPenDotted);
            // 3. 手动绘制贝塞尔曲线
            SetPixel(hdc, controlPoints[0].x, controlPoints[0].y, RGB(0, 200, 0)); // 从起点开始
            float step = 0.01f; // t的步长,值越小曲线越平滑
            for (float t = step; t <= 1.0f; t += step) {
                POINT p = CalculateBezierPoint(t, controlPoints, 4);
                SetPixel(hdc, p.x, p.y, RGB(0, 200, 0));
            }
            EndPaint(hwnd, &ps);
            break;
        }
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

总结与对比

方法 优点 缺点 适用场景
graphics.h 极其简单,代码少,适合教学 过时,平台兼容性差,功能有限 C语言初学者学习图形绘制概念
Win32 API Windows原生,功能强大,性能高,专业 API复杂,代码量多,仅限Windows Windows桌面应用程序开发
手动实现 理解算法核心,不依赖任何库 效率较低,代码复杂,细节多 学习计算机图形学,面试,或需要高度自定义时

推荐选择:

  • 如果你是初学者,只想快速画个图看看:使用 graphics.h
  • 如果你要在Windows上开发一个正式的应用程序:使用 Win32 API
  • 如果你想深入理解贝塞尔曲线的原理:手动实现算法。
-- 展开阅读全文 --
头像
织梦首页如何实现自动更新?
« 上一篇 今天
dede5.7内链插件怎么用?效果如何?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]