Bresenham算法画直线


title: 图形学-直线的扫描转换
date: 2018-10-22 20:31:06
updated: 2018-10-22 20:31:06
description: Bresenham算法画直线
categories: 必修
photo:
tags:

  • CG
    music-id:
    password:
    math:

实验要求

完成中点Bresenham画线,要求如下

  1. 创建名称为学号******的单文档项目,该项目需要一直保留到学期结束
  2. 在主菜单中创建如下菜单,并在视图类中添加直线菜单的事件响应函数
  3. 在视图类中增加鼠标左键按下和弹起事件响应函数
  4. 要求在客户区完成鼠标左键按下确定起点,鼠标左键弹起确定终点并画任意象限的直线。

实验平台

VisualStudio2017
MFC应用程序

中点Bresenham算法原理

设当前屏幕上最逼近直线的像素点为Pi(xi,yi),下一个像素点Pi+1(xi+1,yi+1)相较于前一个像素点的坐标每次在主位移方向上+or-1,另一个方向上是否+or-1取决于中点偏差判别式的值。
若直线斜率绝对值<=1,直线在x方向上变化快,以x为主位移方向。否则,以y为主位移方向。

构造中点误差项

从Pi(xi,yi)点出发选择下一像素时,需将Pu和Pd的中点M(xi+1,yi+0.5)代入隐函数方程,构造中点误差项di。
di = F(xi+1,yi+0.5) = yi+0.5-k(xi+1)-b

当di < 0时,中点M在直线的下方,Pu点离直线距离更近,下一像素点应选取Pu,即y向上走一步;di = 0时,选取两点均可。否则,选取Pd点,y方向上不移动。

因此

Bresenham算法画直线_第1张图片

递推公式

  1. 中点误差项的递推公式

Bresenham算法画直线_第2张图片

  1. 中点误差项的初始值
    直线的起点坐标为P0(x0,y0),x为主位移方向,因此第一个中点是M(x0+1,y0+0.5),相应的di的初始值为

Bresenham算法画直线_第3张图片

关键代码

void CMy1401160120View::MoveLine()
{
	// TODO: 在此添加命令处理程序代码
	CDC *pDC = GetDC();
	COLORREF clr = RGB(255, 0, 0);
	double dx = x1 - x0, dy = y1 - y0;
	int x = Round(x0), y = Round(y0);
	//绘制垂线
	if (fabs(x - x1)<1e-6) {
		if (y0>y1) {
			swap(y0, y1);
			swap(x0, x1);
		}
		for (y = Round(y0); y < Round(y1); y++) {
			pDC->SetPixelV(Round(x), Round(y), clr);
		}
	}
	else {
		double k, d;//k为斜率,d为中点误差项
		k = dy / dx;
		//0= 0.0 && k <= 1.0) {

			if (x0 > x1) {
				swap(x0, x1);
				swap(y0, y1);
			}
			d = 0.5 - k;
			for (x = Round(x0); x < Round(x1); x++) {
				pDC->SetPixelV(Round(x), Round(y), clr);
				if (d < 0) {
					y++;
					d += 1 - k;
				}
				else {
					d -= k;
				}
			}

		}
		if (k > 1) {
			COLORREF clr = RGB(255, 0, 0);
			if (y0 > y1) {
				swap(x0, x1);
				swap(y0, y1);
			}
			d = 1 - 0.5*k;
			for (y = Round(y0); y < Round(y1); y++) {
				pDC->SetPixelV(Round(x), Round(y), clr);
				if (d >= 0) {
					x++;
					d += 1 - k;
				}
				else {
					d += 1;
				}
			}

		}
		if (k >= -1.0 && k <= 0.0) {

			if (x0 > x1) {
				swap(x0, x1);
				swap(y0, y1);
			}
			d = -0.5 - k;
			for (x = Round(x0); x < Round(x1); x++) {
				pDC->SetPixelV(Round(x), Round(y), clr);
				if (d > 0) {
					y--;
					d -= 1 + k;
				}
				else {
					d -= k;
				}
			}

		}
		if (k < -1.0) {

			if (y0 < y1) {
				swap(x0, x1);
				swap(y0, y1);
			}
			d = -1 - 0.5*k;
			for (y = Round(y0); y > Round(y1); y--) {
				pDC->SetPixelV(Round(x), Round(y), clr);
				if (d < 0) {
					x++;
					d -= 1 + k;
				}
				else {
					d -= 1;
				}

			}
		}
	}
}



void CMy1401160120View::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CView::OnLButtonDown(nFlags, point);
	x0 = point.x;
	y0 = point.y;
}


void CMy1401160120View::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	CView::OnLButtonUp(nFlags, point);
	x1 = point.x;
	y1 = point.y;
	MoveLine();
}

结果示例

点击画图——直线
然后按下鼠标左键画直线
结果如下所示:

Bresenham算法画直线_第4张图片

小结

代码逻辑还是挺简单的,但是对vs不熟悉,对于事件响应反而搞了挺半天的,觉得顺手就写下博客记录一下。需要完整代码的自行联系帅气的作者。

你可能感兴趣的:(CG)