MFC之学习Bezier样条使用

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是圆的半径,c=m\times 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函数时,要使两段曲线平滑过渡,要求公共点与前后相邻的控制点,三点同线。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赴星辰大海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值