1.笔记
1.1Bezier曲线
一条三次Bezier样条通过控制4个定义点来定义图形:两个端点和两个控制点。起始点和终止点称为端点,中间的两个点称为控制点。移动端点时Bezier曲线改变曲线的曲率(弯曲的程度),移动中间点(也就是移动虚拟的控制线)时,Bezier曲线在起始点和终止点锁定的情况下做均匀移动。
1.2Bezier样条函数
函数原型:
BOOL CDC::PolyBezier(const POINT* lpPoints,int nCount);
lpPoints是样条的终点和控制点组成的数组,nCount是lpPoints数组中数组元素的个数,每段Bezier样条要求2个控制点和一个终止点,第一个Bezier样条还要求1个附加的起始点。
调用成功返回非零,否则返回零。
2.使用练习
2.1给定7个点P0(100,600),P1(200,100),P2(600,50),P3(700,300),P5(1200,400),P6(1100,200)。使用3像素宽黑色实线画笔控制绘制多边形,使用1像素宽蓝色实线画笔绘制Bezier样条。假设P4的x坐标为800,计算P4的y坐标,并光滑拼接两段Bezier样条。在设备坐标系内编程实现。
void CExample1View::OnDraw(CDC* pDC)
{
CExample1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
CPoint p[7];
p[0] = CPoint(100, 600); p[1] = CPoint(200, 100);
p[2] = CPoint(600, 50); p[3] = CPoint(700, 300);
double k = (p[3].y - p[2].y) / (p[3].x - p[2].x);//开始计算P4的y坐标
double x4 = 800;
double y4 = k * (x4 - p[3].x) + p[3].y;
p[4] = CPoint((int)x4, (int)y4);
p[5] = CPoint(1200, 400); p[6] = CPoint(1100, 120);
CPen penBlack(PS_SOLID, 3, RGB(0, 0, 0));
CPen* pOldPen = pDC->SelectObject(&penBlack);
CBrush brushBlack(RGB(0, 0, 0));
CBrush* pOldBrush = pDC->SelectObject(&brushBlack);
for (int i = 0; i < 7; ++i) {
if (i == 0) pDC->MoveTo(p[i]);
else pDC->LineTo(p[i]);
//强调顶点
pDC->Ellipse(p[i].x - 5, p[i].y - 5, p[i].x + 5, p[i].y + 5);
}
pDC->SelectObject(pOldPen);
CPen penBlue(PS_SOLID, 1, RGB(0, 0, 255));
pOldPen = pDC->SelectObject(&penBlue);
pDC->PolyBezier(p, 7);//绘制样条
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
}
绘制效果:
2.2给定4个点P0(0,r),P1(c,r),P2(r,c),P3(r,0)可以生成一段三次样条,该样条曲线可以模拟四分之一圆,根据对称性使用该方法可以模拟整圆,r是圆的半径,
,m被称为魔术常数,约等于0.5523,在自定义坐标系中实现。
题意图示:
编程实现:
void CExample1View::OnDraw(CDC* pDC)
{
CExample1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
CRect rect;
GetClientRect(rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(), rect.Height());
pDC->SetViewportExt(rect.Width(), -rect.Height());
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);
double m = 4 * (sqrt(2.0) - 1) / 3;//魔术常数
double r = 300, c = m * r;
CPoint p[13];
p[0] = CPoint(0, (int)r);
p[1] = CPoint((int)c, r);
p[2] = CPoint(r, (int)c);
p[3] = CPoint(r, 0);
p[4] = CPoint(r, (int)-c);
p[5] = CPoint((int)c, -r);
p[6] = CPoint(0, (int)-r);
p[7] = CPoint((int)-c, -r);
p[8] = CPoint(-r, (int)-c);
p[9] = CPoint(-r, 0);
p[10] = CPoint(-r, (int)c);
p[11] = CPoint(-c, (int)r);
p[12] = CPoint(0, r);
CPen newPen, * pOldPen;
newPen.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
pOldPen = pDC->SelectObject(&newPen);
pDC->PolyBezier(p, 13);//绘制样条曲线
pDC->SelectObject(pOldPen);
newPen.DeleteObject();
newPen.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
pOldPen = pDC->SelectObject(&newPen);
for (int i = 0; i < 13; ++i) {//绘制虚拟控制线
if (i == 0) pDC->MoveTo(p[i]);
else pDC->LineTo(p[i]);
}
pDC->SelectObject(pOldPen);
}
实现效果:
3.总结
PolyBezier样条函数参数中数组元素的个数要满足3n+1,n是样条段数。
连续绘制多段Bezier函数时,要使两段曲线平滑过渡,要求公共点与前后相邻的控制点,三点同线。