DDA 画线算法 - C++ 实现

DDA 算法

DDA 算法解决这样一个问题:给定屏幕上的两个像素点,画出以这两点为端点的线段。

首先定义最左上角的像素点坐标为 (0, 0),y 轴方向向下,x 轴方向向右,于是坐标 (y, x) 表示屏幕上第 y 行,第 x 列的像素点。令输入的两个点为 A (y1, x1),B (y2, x2)。

考虑一种特殊的情况:A 在 B 的左下角,并且线段的斜率 0 < k < 1:

  1. 确定需要绘制的像素数量。由于斜率 k 小于 1,所以在 x ∈ [ x 1 , x 2 ] x\in[x1, x2] x[x1,x2] 的闭区间上,对每一个横坐标 x,找到合适的高度 y,绘制像素 (y, x),所以一共需要绘制 x2-x1+1 个像素点。我们通过变量 x 遍历这个区间来绘制线段。
  2. 对每一个横坐标 x,如何确定要绘制的 y?一般的做法是通过直线方程计算,但是运算量较大。假设从 A 开始,绘制的第一个像素是 A (y1, x1),下一个 x = x1 + 1,容易得到此时的 y = y1 + k,这样在循环中通过步进的方式,用加法代替了乘法。
  3. 这时的 y 还是浮点数, ⌊ y + 0.5 ⌋ \lfloor y+0.5\rfloor y+0.5 得到的整数即为最接近 y 的整数,这样就确定了每一次循环中要绘制的 (y, x)。

为什么斜率 0 < k < 1 的时候要对每一个 x ∈ [ x 1 , x 2 ] x\in[x1, x2] x[x1,x2] 绘制,而不是 y ∈ [ y 1 , y 2 ] y\in[y1, y2] y[y1,y2] 呢?因为如果用后面的方法,点会变得很稀疏,所以如果是斜率 k > 1 的情况,采用 y ∈ [ y 1 , y 2 ] y\in[y1, y2] y[y1,y2] 的区间,并用变量 y 遍历区间,来确定每一个 y 对应的 x 就可以了。

完整代码如下,drawPixel 前两个参数代表要绘制的像素坐标,第三个参数是像素的颜色。

C++ 代码

void drawLine(const int y1, const int x1, const int y2, const int x2, const Color& c) {
	if (y1 == y2 && x1 == x2) {
		drawPixel(y1, x1, c);
	} else if (y1 == y2) {
		int inc = x2 > x1 ? 1 : -1;
		for (int x = x1; x != x2; x += inc)
			drawPixel(y1, x, c);
	} else if (x1 == x2) {
		int inc = y2 > y1 ? 1 : -1;
		for (int y = y1; y != y2; y += inc)
			drawPixel(y, x1, c);
	} else {
		// 基于DDA算法
		double y = y1, x = x1, dy = y2 - y1, dx = x2 - x1;
		if (abs(dx) > abs(dy)) {
			// x主导方向
			int steps = abs(dx) + 1;
			double incY = dy / abs(dx);
			double incX = x2 > x1 ? 1 : -1;
			for (int i = 0; i < steps; ++i, y += incY, x += incX)
				drawPixel(y + 0.5, x, c);
		} else {
			// y主导方向
			int steps = abs(dy) + 1;
			double incY = y2 > y1 ? 1 : -1;
			double incX = dx / abs(dy);
			for (int i = 0; i < steps; ++i, y += incY, x += incX)
				drawPixel(y, x + 0.5, c);
		}
	}
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值