参考:https://www.bilibili.com/video/BV1ud4y1C7iA
1.Bernstrin基函数算法
void DrawCurve1(HDC hdc) // 基函数绘制曲线
{
MoveToEx(hdc, ROUND(P[0].x), ROUND(P[0].y), NULL);
double tStep = 0.01;
for (double t = 0; t <= 1; t += tStep)
{
double B03 = (1 - t) * (1 - t) * (1 - t);
double B13 = 3 * t * (1 - t) * (1 - t);
double B23 = 3 * t * t * (1 - t) ;
double B33 = t * t * t;
Point2 pt = B03 * P[0] + B13 * P[1] + B23 * P[2] + B33 * P[3];
LineTo(hdc,ROUND(pt.x),ROUND(pt.y));
}
}
2.de Casteljau 递推算法
void DrawCurve(HDC hdc) // de Casteljau 递推算法
{
MoveToEx(hdc, ROUND(P[0].x), ROUND(P[0].y), NULL);
double tStep = 0.01;
for (double t = 0; t <= 1; t += tStep)
{
Point2 p0, p1, p2, p3, p4, p5;
p0 = (1 - t) * P[0] + t * P[1];
p1 = (1 - t) * P[1] + t * P[2];
p2 = (1 - t) * P[2] + t * P[3];
p3 = (1 - t) * p0 + t * p1;
p4 = (1 - t) * p1 + t * p2;
p5 = (1 - t) * p3 + t * p4;
LineTo(hdc, ROUND(p5.x), ROUND(p5.y));
}
}
完成代码
// 25-三次Beizer曲线算法
// 参考 https://www.bilibili.com/video/BV1ud4y1C7iA
#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"25-三次Beizer曲线算法"
#define ROUND(d) int(floor(d)+0.5) // 四舍五入
#define PI 3.1415926
struct Point2 // 二维点
{
double x;
double y;
double w; // 齐次坐标
Point2() :x(0), y(0), w(0) {}
Point2(double x, double y) :x(x), y(y), w(0) {}
friend Point2 operator + (Point2 pt0, Point2 pt1 )
{
return Point2(pt0.x + pt1.x, pt0.y + pt1.y);
}
friend Point2 operator * (Point2 pt, double n)
{
return Point2(pt.x * n, pt.y * n);
}
friend Point2 operator * ( double n, Point2 pt)
{
return Point2(pt.x * n, pt.y * n);
}
};
class CubicBezierCurve
{
public:
Point2 P[4]; // 控制点数组
public:
CubicBezierCurve(){}
~CubicBezierCurve(){}
void ReadPoint(Point2 * p) // 读入控制点
{
for (int nPoint = 0; nPoint < 4; nPoint++)
{
P[nPoint] = p[nPoint];
}
}
void DrawCurve1(HDC hdc) // 基函数绘制曲线
{
MoveToEx(hdc, ROUND(P[0].x), ROUND(P[0].y), NULL);
double tStep = 0.01;
for (double t = 0; t <= 1; t += tStep)
{
double B03 = (1 - t) * (1 - t) * (1 - t);
double B13 = 3 * t * (1 - t) * (1 - t);
double B23 = 3 * t * t * (1 - t) ;
double B33 = t * t * t;
Point2 pt = B03 * P[0] + B13 * P[1] + B23 * P[2] + B33 * P[3];
LineTo(hdc,ROUND(pt.x),ROUND(pt.y));
}
}
void DrawCurve(HDC hdc) // de Casteljau 递推算法
{
MoveToEx(hdc, ROUND(P[0].x), ROUND(P[0].y), NULL);
double tStep = 0.01;
for (double t = 0; t <= 1; t += tStep)
{
Point2 p0, p1, p2, p3, p4, p5;
p0 = (1 - t) * P[0] + t * P[1];
p1 = (1 - t) * P[1] + t * P[2];
p2 = (1 - t) * P[2] + t * P[3];
p3 = (1 - t) * p0 + t * p1;
p4 = (1 - t) * p1 + t * p2;
p5 = (1 - t) * p3 + t * p4;
LineTo(hdc, ROUND(p5.x), ROUND(p5.y));
}
}
void DrawPolygon(HDC hdc) // 绘制控制多边形
{
HPEN newPen=CreatePen(PS_SOLID,3,RGB(0,0,180));
HGLOBAL oldPen=SelectObject(hdc,newPen);
MoveToEx(hdc, ROUND(P[0].x), ROUND(P[0].y), NULL);
Ellipse(hdc, ROUND(P[0].x)-5, ROUND(P[0].y) - 5, ROUND(P[0].x) + 5, ROUND(P[0].y) + 5);
for (int i = 0; i < 4; i++)
{
LineTo(hdc, ROUND(P[i].x) , ROUND(P[i].y));
Ellipse(hdc, ROUND(P[i].x) - 5, ROUND(P[i].y) - 5, ROUND(P[i].x) + 5, ROUND(P[i].y) + 5);
}
SelectObject(hdc, oldPen);
DeletePen(newPen);
}
};
static bool m_Play = false;
static wchar_t* m_str = L"";
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
static double alpha;
static double beta;
switch (uMsg)
{
case WM_TIMER:
alpha += 1;
beta += 1;
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_LBUTTONDOWN:
m_Play = !m_Play;
if (m_Play)
{
SetTimer(hwnd, 0, 0, NULL);
}
else
{
KillTimer(hwnd, 0);
}
return 0;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, rect.right, rect.bottom, NULL);
SetViewportExtEx(hdc, rect.right, -rect.bottom, NULL);
SetViewportOrgEx(hdc, rect.right / 2, rect.bottom / 2, NULL);
// 内存DC
HDC memDC = CreateCompatibleDC(hdc); // 创建兼容DC 画板
HBITMAP newBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); // 创建画布
HGDIOBJ oldBitmap = SelectObject(memDC, newBitmap); // 将画布选入画板
FillRect(memDC, &rect, (HBRUSH)(COLOR_WINDOW + 1));
DrawText(memDC, m_str, wcslen(m_str), &rect, NULL);
SetMapMode(memDC, MM_ANISOTROPIC);
SetWindowExtEx(memDC, rect.right, rect.bottom, NULL);
SetViewportExtEx(memDC, rect.right, -rect.bottom, NULL);
SetViewportOrgEx(memDC, rect.right / 2, rect.bottom / 2, NULL);
// 绘制图形
{
Point2 P[4] =
{
Point2(-300, -300),
Point2(-200, 100),
Point2( 300, 250),
Point2( 400, -200),
};
CubicBezierCurve bezier; // 贝塞尔曲线
bezier.ReadPoint(P); // 读取控制点
bezier.DrawPolygon(memDC); // 绘制控制线
// bezier.DrawCurve(memDC); // de Casteljau 递推算法
bezier.DrawCurve1(memDC); // 基函数绘制曲线
}
// 内存dc复制到设备
BitBlt(hdc, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), rect.right, rect.bottom, memDC, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), SRCCOPY);
SelectObject(memDC, oldBitmap);
DeleteObject(newBitmap);
DeleteDC(memDC);
EndPaint(hwnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"CAG";
WNDCLASS wc = { };
wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
WINDOW_TEXT, // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}