1. 实验内容
1. 绘制三次Bezier曲线
(1)给定四个已知点P1—P4,以此作为控制顶点绘制一段三次Bezier曲线。
(2)给定四个已知点P1—P4,以此作为曲线上的点绘制一段三次Bezier曲线。
2.绘制三次B样条曲线
给定六个已知点P1—P6,以此作为控制顶点绘制一条三次B样条曲线。
2. 实验环境
Visual Studio 2019
图形学实验程序框架
Windows11系统
3. 问题分析
3.1问题1(1)
对于问题1(1),三次Bezier曲线的矩阵表示形式如下:
3.2问题1(2)
解得:
3.3问题2
对于问题2,三次B样条曲线的矩阵表示形式如下:
一段三次B样条曲线需要4个控制点。但在已有三次B样条曲线的情况下,可以额外增加一个控制点,即可相应地增加一段B样条曲线,自然地能达到C 2 C_2C2连续。
4. 算法设计
4.1问题1(1)
正如3.1所分析,算法的流程如下:
3.循环第2步,直到t = 1 t=1t=1时结束。
算法的流程图如下:
4.2问题2
正如章节3.2分析,算法的流程如下:
3.循环第2步,直到t=1时结束。
4.根据(10)式反推控制点,并将4个控制点连结成线。
4.3问题3
5. 源代码
5.1 Bezier1
//以已知的四个点为控制点绘制Bezier曲线 //p:已知的四个控制点 void CDiamondView::DrawBezier1(POINT p[4]) { InvalidateRect(NULL);//强制清屏 UpdateWindow(); CDC* pDC = GetDC(); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); oldPen = pDC->SelectObject(&newPen); pDC->Polyline(p, 4); pDC->SelectObject(oldPen); CPen newPen2; newPen2.CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); oldPen = pDC->SelectObject(&newPen2); double ax = -p[0].x + 3 * p[1].x - 3 * p[2].x + p[3].x; double ay = -p[0].y + 3 * p[1].y - 3 * p[2].y + p[3].y; double bx = 3 * p[0].x - 6 * p[1].x + 3 * p[2].x; double by = 3 * p[0].y - 6 * p[1].y + 3 * p[2].y; double cx = -3 * p[0].x + 3 * p[1].x; double cy = -3 * p[0].y + 3 * p[1].y; double dx = p[0].x; double dy = p[0].y; pDC->MoveTo(p[0].x, p[0].y); for (double t = 0; t <= 1; t = t + 0.001) { int xt = ax * t * t * t + bx * t * t + cx * t + dx; int yt = ay * t * t * t + by * t * t + cy * t + dy; pDC->LineTo(xt, yt); // Sleep(10); } pDC->SelectObject(oldPen); }
5.2 Bezier2
//以已知的四个点为Bezier曲线上的点来绘制Bezier曲线 //p:已知的四个点 void CDiamondView::DrawBezier2(POINT p[4]) { //假设控制点1的t1=0.25 //假设控制点2的t2=0.5 InvalidateRect(NULL);//强制清屏 UpdateWindow(); CDC* pDC = GetDC(); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); oldPen = pDC->SelectObject(&newPen); pDC->Polyline(p, 4); pDC->SelectObject(oldPen); POINT controlpoint[4]; controlpoint[0] = p[0];//控制点0即为P0 controlpoint[3] = p[3];//控制点1即为P1 controlpoint[1].x = 3.5549 * (p[1].x - 0.4219 * p[0].x - 0.0156 * p[3].x) - 1.3329 * (p[2].x - 0.125 * p[0].x - 0.125 * p[3].x); controlpoint[1].y = 3.5549 * (p[1].y - 0.4219 * p[0].y - 0.0156 * p[3].y) - 1.3329 * (p[2].y - 0.125 * p[0].y - 0.125 * p[3].y);//反推控制点1坐标 controlpoint[2].x = -3.5549*(p[1].x - 0.4219 * p[0].x - 0.0156 * p[3].x) + 3.9995 * (p[2].x - 0.125 * p[0].x - 0.125 * p[3].x); controlpoint[2].y = -3.5549 * (p[1].y - 0.4219 * p[0].y - 0.0156 * p[3].y) + 3.9995 * (p[2].y - 0.125 * p[0].y - 0.125 * p[3].y);//反推控制点2坐标 CPen newPen1; newPen1.CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); oldPen = pDC->SelectObject(&newPen1); pDC->Polyline(controlpoint, 4); pDC->MoveTo(p[0].x, p[0].y); pDC->SelectObject(oldPen); CPen newPen2; newPen2.CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); oldPen = pDC->SelectObject(&newPen2); //反推ax、bx、cx、dx、ay、by、cy、dy double ax = 8.0 / 3.0 * p[3].x - 16 * p[2].x + 64.0 / 3.0 * p[1].x - 8 * p[0].x; double ay = 8.0 / 3.0 * p[3].y - 16 * p[2].y + 64.0 / 3.0 * p[1].y - 8 * p[0].y; double bx = -2 * p[3].x + 20 * p[2].x - 32 * p[1].x + 14 * p[0].x; double by = -2 * p[3].y + 20 * p[2].y - 32 * p[1].y + 14 * p[0].y; double cx = -7 * p[0].x + 32.0 / 3.0 * p[1].x - 4 * p[2].x + 1.0 / 3.0 * p[3].x; double cy = -7 * p[0].y + 32.0 / 3.0 * p[1].y - 4 * p[2].y + 1.0 / 3.0 * p[3].y; double dx = p[0].x; double dy = p[0].y; for (double t = 0; t <= 1; t = t + 0.001) { int xt = ax * t * t * t + bx * t * t + cx * t + dx; int yt = ay * t * t * t + by * t * t + cy * t + dy; pDC->LineTo(xt, yt); // Sleep(10); } pDC->SelectObject(oldPen); }
5.3 B样条曲线
//以已知的六个点为控制点来绘制B样条曲线 //p:已知的六个控制点 void CDiamondView::DrawBCurve(POINT p[6]) { InvalidateRect(NULL);//强制清屏 UpdateWindow(); CDC* pDC = GetDC(); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); oldPen = pDC->SelectObject(&newPen); pDC->Polyline(p, 6); pDC->SelectObject(oldPen); CPen newPen2; newPen2.CreatePen(PS_SOLID, 3, RGB(255, 0, 0)); oldPen = pDC->SelectObject(&newPen2); for (int i = 0; i < 3; i++) { double ax = -(p[i].x - 3 * p[i + 1].x + 3 * p[i + 2].x - p[i + 3].x) / 6; double ay = -(p[i].y - 3 * p[i + 1].y + 3 * p[i + 2].y - p[i + 3].y) / 6; double bx = (p[i].x - 2 * p[i + 1].x + p[i + 2].x) / 2; double by = (p[i].y - 2 * p[i + 1].y + p[i + 2].y) / 2; double cx = -(p[i].x - p[i + 2].x) / 2; double cy = -(p[i].y - p[i + 2].y) / 2; double dx = (p[i].x + 4 * p[i + 1].x + p[i + 2].x) / 6; double dy = (p[i].y + 4 * p[i + 1].y + p[i + 2].y) / 6; // pDC->MoveTo(p[i].x, p[i].y); for (double t = 0; t <= 1; t = t + 0.001) { int xt = ax * t * t * t + bx * t * t + cx * t + dx; int yt = ay * t * t * t + by * t * t + cy * t + dy; pDC->MoveTo(xt, yt); pDC->LineTo(xt, yt); // Sleep(10); } } pDC->SelectObject(oldPen); }
6.程序运行结果
正确地根据4个控制点绘制出了一条Bezier曲线,且将4个控制点连接绘制出来。
正确地根据曲线上4个点绘制出了一条Bezier曲线能够穿过这4个点,且将这4个点连接绘制出来。并且能够根据曲线本身反推控制点,将这4个点用蓝色的线连接绘制出来。
正确地根据6个控制点绘制了B样条曲线。