Nicolas Lee 软件折腾工程师

2019-03-21

博客一(修改版):

Bezier曲线分为一次/二次/三次/多次贝塞尔曲线,之所以这么分是为了更好的理解其中的内涵。

一次贝塞尔曲线(线性Bezier),实际上就是一条连接两点的直线段。 二次贝塞尔曲线,就是两点间的一条抛物线,利用一个控制点来控制抛物线的形状。 三次贝塞尔曲线,则需要一个起点,一个终点,两个控制点来控制曲线的形状。 实例如下图:

img点击并拖拽以移动

通用的贝塞尔曲线的生成算法,可以简单表示如下:

struct Point_Float  
{  
    float x;   
    float y;  
};   
  
// CalculateBeizer 以控制点 cp 所产生的曲线点,填入 Point_Float 结构数组。   
// 调用方必须分配足够的空间以供输出,<sizeof(Point_Float) numOfPoints>  
void CalculateBeizer(Point_Float* cp, int numOfPoints, Point_Float* curve)  
{  
    float t;  
    int i;   
    t = 1.0/(numOfPoints - 1);  
    for(i = 0; i < numOfPoints; i++)   
        curve[i] = PointOnCubicBezier(cp, i*t);  
}  
  
// 参数1: 4个点坐标(起点,控制点1,控制点2,终点)  
// 参数2: 0 <= t <= 1   
Point_Float PointOnCubicBezier(Point_Float* cp, float t)   
{   
    Point_Float tPoint;   
    tPoint.x = MetaComputing(cp[0].x, cp[1].x, cp[2].x, cp[3].x, t);  
    tPoint.y = MetaComputing(cp[0].y, cp[1].y, cp[2].y, cp[3].y, t);  
    return tPoint;   
}   
  
float MetaComputing(float p0, float p1, float p2, float p3, float t)  
{  
    // 方法一:  
    float a, b, c;   
    float tSquare, tCube;   
    // 计算多项式系数    
    c = 3.0 * (p1 - p0);   
    b = 3.0 * (p2 - p1) - c;   
    a = p3 - b - c - p0;   
  
    // 计算t位置的点   
    tSquare = t * t;   
    tCube   = t * tSquare;   
    return (a * tCube) + (b * tSquare) + (c * t) + p0;  
  
    // 方法二: 原始的三次方公式  
    //  float n = 1.0 - t;  
    //  return n*n*n*p0 + 3.0*p1*t*n*n + 3.0*p2*t*t*n + p3*t*t*t;  
}  

通过该算法可以方便的实现点插值~ 因而,就有了光滑的曲线。

当然又基于此,有许多改进的方法来快速实现曲线生成。

博客二(修改版):

演示调用系统API函数PolyBezier()画线。

#include<windows.h>  
#include<stdlib.h>  
#include<time.h>  
#define NUM 10  
  
LRESULT CALLBACK Winproc(HWND,UINT,WPARAM,LPARAM);  
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstanc,LPSTR lpCmdLine,int nShowCmd)  
{  
    MSG msg;  
    static TCHAR szClassName[] = TEXT("::Bezier样条计算公式由法国雷诺汽车公司的工程师Pierm Bezier于六十年代提出");  
    HWND hwnd;  
    WNDCLASS wc;  
    wc.cbClsExtra =0;  
    wc.cbWndExtra =0;  
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);  
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);  
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);  
    wc.hInstance = hInstance;  
    wc.lpfnWndProc = Winproc;  
    wc.lpszClassName = szClassName;  
    wc.lpszMenuName = NULL;  
    wc.style = CS_HREDRAW|CS_VREDRAW;  
  
    if(!RegisterClass(&wc))  
    {  
        MessageBox(NULL,TEXT("注册失败"),TEXT("警告框"),MB_ICONERROR);  
        return 0;  
    }  
    hwnd = CreateWindow(szClassName,szClassName,  
        WS_OVERLAPPEDWINDOW,  
        CW_USEDEFAULT,CW_USEDEFAULT,  
        CW_USEDEFAULT,CW_USEDEFAULT,  
        NULL,NULL,hInstance,NULL);  
  
    ShowWindow(hwnd,SW_SHOWMAXIMIZED);  
    UpdateWindow(hwnd);  
  
    while(GetMessage(&msg,NULL,0,0))  
    {  
        TranslateMessage(&msg);  
        DispatchMessage(&msg);  
    }  
    return msg.wParam;  
}  
  
LRESULT CALLBACK Winproc(HWND hwnd,UINT message, WPARAM wparam,LPARAM lparam)  
{  
    PAINTSTRUCT ps;  
    HDC hdc;  
    static POINT pt[NUM];  
    TEXTMETRIC tm;  
    static int cxClient,cyClient;  
    HPEN hpen;  
    int i,j,k,n,t;  
  
    switch(message)  
    {  
    case WM_CREATE:  
        static int cxchar;  
        hdc = GetDC(hwnd);  
        GetTextMetrics(hdc,&tm);  
        cxchar = tm.tmAveCharWidth;  
        ReleaseDC(hwnd,hdc);  
  
    case WM_SIZE:  
        cxClient = LOWORD(lparam);  
        cyClient = HIWORD(lparam);  
        return 0;  
    case WM_PAINT:  
        hdc = GetDC(hwnd);  
        srand(time(0));  
  
        Rectangle(hdc,0,0,cxClient,cyClient);  
        for(i=0; i<500; i++)  
        {  
            SelectObject(hdc,GetStockObject(WHITE_PEN));  
            PolyBezier(hdc,pt,NUM);  
            for(j=0; j<NUM; j++)  
            {  
                pt[j].x = rand()%cxClient;  
                pt[j].y = rand()%cyClient;  
            }  
            hpen = CreatePen(PS_INSIDEFRAME,3,RGB(rand()%256,rand()%256,rand()%256));  
            DeleteObject(SelectObject(hdc,hpen));  
            PolyBezier(hdc,pt,NUM);  
            for(k=0; k<50000000;k++);  
        }  
        for(i=0; i<100;i++)  
        {  
            Ellipse(hdc,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient);  
  
            Pie(hdc,j=rand()%cxClient,k=rand()%cyClient,n=rand()%cxClient,t=rand()%cyClient,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient) ;   
  
        }  
        if((n=(n+j)/2)>cxchar*20) n=cxchar*20;    
        SetTextColor(hdc,RGB(rand()%256,rand()%256,rand()%256));  
        TextOut(hdc,n/2,(t+k)/2,TEXT("瑾以此向Pierm Bezier致敬!"),lstrlen(TEXT("瑾以此向Pierm Bezier致敬!")));  
        ReleaseDC(hwnd,hdc);  
        DeleteObject(hpen);  
        ValidateRect(hwnd,NULL);  
        return 0;  
  
    case WM_DESTROY:  
        PostQuitMessage(0);  
        return 0;  
    }  
    return DefWindowProc(hwnd,message,wparam,lparam);  
}  


Content