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方向上不移动。

因此

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

关键代码

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<k<1
		if (k >= 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();
}

结果示例

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

在这里插入图片描述

小结

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

以下是用C语言实现Bresenham算法直线的代码示例: ### Bresenham算法 C语言 实现 #### 说明 该算法适用于绘制任意斜率的第一象限内的直线。对于其他象限的情况,可以扩展此逻辑以处理不同的方向。 ```c #include <graphics.h> // 使用 graphics 库进行绘图 #include <stdio.h> void drawLine(int x0, int y0, int x1, int y1) { int dx = abs(x1 - x0), dy = abs(y1 - y0); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int err = dx - dy; while (1) { putpixel(x0, y0, WHITE); // 绘制像素点 if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } } int main() { int gd = DETECT, gm; initgraph(&gd, &gm, NULL); // 初始化图形模式 int x0 = 100, y0 = 100, x1 = 300, y1 = 200; // 定义起始点和结束点 drawLine(x0, y0, x1, y1); getch(); // 等待按键输入 closegraph(); // 关闭图形窗口 return 0; } ``` --- #### 解析 上述代码实现了完整的Bresenham算法用于绘制直线的功能[^3]。 - `dx` 和 `dy` 表示两个端点之间的水平距离和垂直距离。 - `sx` 和 `sy` 是步长变量,用来决定移动的方向(正向或反向)。 - `err` 是误差项,用于判断下一步应该增加哪个维度的坐标值。 - 循环条件确保当到达目标点时停止绘制。 这种实现方法不仅支持第一象限中的直线绘制,还能够适应不同象限以及负斜率情况下的直线绘制需求[^4]。 --- ### 相关问题
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值