计算机图像学学习记录(二) —— 中点画线算法

中点画线算法

为了方便阅读算法代码的人,现在这贴上算法核心代码:
算法过程:
注意:本过程只针对,斜率绝对值小于1的情况。

void DDADrawLine::MPDrawLine(int x0, int y0, int x1, int y1)
{
	int a, b, dt1, dt2, d, x, y;
	a = y0 - y1;
	b = x1 - x0;
	d = a + a + b; //为了避免小数,这里取2倍 不写成2*a + b的原因是防止乘法
	dt1 = a + b + a + b;
	dt2 = a + a;
	x = x1;
	y = y1;
	// 绘制起点
//	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
	glVertex2i(x, y);
//	glEnd();
	// 绘制整条线
	while (x < x1)
	{
		if (d < 0)
		{
			x++;
			y++;
			d += dt1;
		}
		else
		{
			x++;
			d += dt2;
		}
		glBegin(GL_POINTS);
		glVertex2i(x, y);
		glEnd();
	}
}

如果针对全部斜率的情况,该算法改进结果如下:

void DDADrawLine::MPLineDraw(int x0, int y0, int x1, int y1)
{
	int x = x0, y = y0;
	int a = y0 - y1;
	int b = x1 - x0;
	int cx = (b >= 0 ? 1 : (b = -b, -1));
	int cy = (a <= 0 ? 1 : (a = -a, -1));

//	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
	glVertex2i(x, y);
	glEnd();
	int d, d1, d2;
	if (-a <= b) // 斜率绝对值 <= 1
	{
		d = a + a + b;
		d1 = a + a;
		d2 = a + a + b + b;
		while (x != x1)
		{
			if (d < 0)
			{
				y += cy;
				d += d2;
			}
			else
			{
				d += d1;
			}
			x += cx;
			glBegin(GL_POINTS);
			glVertex2i(x, y);
			glEnd();
		}
	}
	else // 斜率绝对值 > 1
	{
		d = a + b + b;
		d1 = b + b;
		d2 = a + a + b + b;
		if (d < 0)
		{
			d += d1;
		}
		else
		{
			x += cx;
			d += d2;
		}
		y += cy;
		glBegin(GL_POINTS);
		glVertex2i(x, y);
		glEnd();
	}
}

原理

中点画线算法采用直线的一般式方程:

F ( x , y ) = 0 A x + B y + C = 0 A = − ( Δ y ) ; B = ( Δ x ) ; C = − B ( Δ x ) \begin {aligned} F(x, y) = 0 \\Ax + By + C = 0 \\A = -(\Delta y); B = (\Delta x); C = -B(\Delta x) \end {aligned} F(x,y)=0Ax+By+C=0A=(Δy);B=(Δx);C=B(Δx)
对于不同的点,我们可以利用其值和0进行比较来筛选。
中点画线图

  • 对于直线上的点 F ( x , y ) = 0 F(x, y) = 0 F(x,y)=0
  • 对于直线上方的点 F ( x , y ) &gt; 0 F(x, y) &gt; 0 F(x,y)>0
  • 对于直线下方的点 F ( x , y ) &lt; 0 F(x, y) &lt; 0 F(x,y)<0

所以,中点画线算法在每次最大位移方向上走一步,而另外一个方向是走步还是不走,需要利用中点误差项来进行判断。
我们假定: 0 ≤ ∣ k ∣ ≤ 1 0\leq|k|\leq1 0k1。 因此,每次x方向上加1, y 方向上是否加1取决于函数值与中点方位。
这里写图片描述
当M在Q的下方,则 P u P_u Pu离直线比较近,应为下一个像素点。
当M在Q的上方,则 P d P_d Pd为下一点。
那么我们如何判断M是在Q的上方还是下方呢?
我们就需要把M代入到理想直线方程中:
F ( x m , y m ) = A x m + B y m + C F(x_m, y_m) = Ax_m + By_m + C F(xm,ym)=Axm+Bym+C
KaTeX parse error: Expected & or \\ or \cr or \end at position 40: … F(x_m, y_m) \\\̲ ̲&=F(x_i + 1, y_…

当 d > 0的时候, M在Q的上方,取 P u P_u Pu
当 d < 0的时候, M在Q的下方,取 P d P_d Pd
当 d = 0的时候, 取 P d P_d Pd P u P_u Pu都可以
那么我们可以得到中点画线法的基本原理,公式如下:
y = { y + 1 d &lt; 0 y d ≥ 0 y = \begin {cases} y + 1 &amp; d &lt; 0 \\ y &amp; d\geq0 \end {cases} y={y+1yd<0d0

算法改进

我们来分析一下该算法的计算量
y = { y + 1 d &lt; 0 y d ≥ 0 d i = A ( x i + 1 ) + B ( y i + 0.5 ) + C y = \begin {cases} y + 1 &amp; d &lt; 0 \\ y &amp; d\geq0 \end {cases} \\ d_i = A(x_i + 1) + B(y_i + 0.5) + C y={y+1yd<0d0di=A(xi+1)+B(yi+0.5)+C
我们发现,为了求出d的值,需要进行两个乘法,四个加法。似乎比DDA算法大了很多的计算量,那么我们该如何改进呢?
我们将利用求d的递推公式,将计算量简化为一个整数加法级别。
这里写图片描述
对于上面这种情况:
KaTeX parse error: Expected & or \\ or \cr or \end at position 49: …}, y_{m_0}) \\\̲ ̲&=F(x_i + 1, y_…

KaTeX parse error: Expected & or \\ or \cr or \end at position 49: …}, y_{m_1}) \\\̲ ̲&=F(x_i + 2, y_…
这里写图片描述
对于上面这种情况:
KaTeX parse error: Expected & or \\ or \cr or \end at position 49: …}, y_{m_1}) \\\̲ ̲&=F(x_i + 2, y_…

接下来我们计算d的初始值:
KaTeX parse error: Expected & or \\ or \cr or \end at position 52: …y——0 + 0.5) \\\̲ ̲&= A(x_0 + 1) +…

那么,可以得到:
d n e w = { d o l d + A + B d &lt; 0 d o l d + A d ≥ 0 d 0 = A + 0.5 B d_{new} = \begin {cases} d_{old} + A + B &amp; d &lt; 0 \\ d_{old} + A &amp; d \geq0 &amp;d_0 = A + 0.5B \end {cases} dnew={dold+A+Bdold+Ad<0d0d0=A+0.5B
之后再利用2d代替d来摆脱浮点运算,写出仅仅包含整数的算法,这样中点画线算法就提高到了整数加法,优于DDA算法。

接下来来看中点画线算法的代码实现:
图形初始化过程:

// 实现DDA画线算法测试
// author: 赵天宇
// date : 2018/03/09
void DDADrawLine::MPTest(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	using namespace std;
//	cout << "输入线段起始和终点坐标(范围 0 - 500, 0 - 500):";
//	cin >> xs >> ys >> xe >> ye;
	srand((unsigned)time(NULL));
	xs = random(0, 500);
	ys = random(0, 500);
	xe = random(0, 500);
	ye = random(0, 500);
	glutInitWindowPosition(50, 100);
	glutInitWindowSize(500, 500);
	glutCreateWindow("Digital Differential  Analyser  Line");
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glMatrixMode(GL_PROJECTION);
	gluOrtho2D(0.0, 500, 0.0, 500.0);
	glutDisplayFunc(DDADrawLine::display);
	DDADrawLine::myInit();
//	DDADrawLine::MPDrawLine(xs, ye, xe, ys);
	glutMainLoop();
}

显示过程:

void DDADrawLine::display(void)
{
	glClear(GL_COLOR_BUFFER_BIT);  /*clear the window */

								   /*----------------------------------------*/
								   /*  viewport stuff                        */
								   /*----------------------------------------*/
								   /* set up a viewport in the screen window */
								   /* args to glViewport are left, bottom, width, height */
	glViewport(0, 0, 500, 500);
	/* NB: default viewport has same coords as in myinit, */
	/* so this could be omitted: */

	
//	DDADrawLine::DDALine(xs, ys, xe, ye);
	DDADrawLine::MPDrawLine(xs, ys, xe, ye);
	/* and flush that buffer to the screen */
	glFlush();
}

算法过程:
注意:本过程只针对,斜率绝对值小于1的情况。

void DDADrawLine::MPDrawLine(int x0, int y0, int x1, int y1)
{
	int a, b, dt1, dt2, d, x, y;
	a = y0 - y1;
	b = x1 - x0;
	d = a + a + b; //为了避免小数,这里取2倍 不写成2*a + b的原因是防止乘法
	dt1 = a + b + a + b;
	dt2 = a + a;
	x = x1;
	y = y1;
	// 绘制起点
//	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
	glVertex2i(x, y);
//	glEnd();
	// 绘制整条线
	while (x < x1)
	{
		if (d < 0)
		{
			x++;
			y++;
			d += dt1;
		}
		else
		{
			x++;
			d += dt2;
		}
		glBegin(GL_POINTS);
		glVertex2i(x, y);
		glEnd();
	}
}

如果针对全部斜率的情况,该算法改进结果如下:

void DDADrawLine::MPLineDraw(int x0, int y0, int x1, int y1)
{
	int x = x0, y = y0;
	int a = y0 - y1;
	int b = x1 - x0;
	int cx = (b >= 0 ? 1 : (b = -b, -1));
	int cy = (a <= 0 ? 1 : (a = -a, -1));

//	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
	glVertex2i(x, y);
	glEnd();
	int d, d1, d2;
	if (-a <= b) // 斜率绝对值 <= 1
	{
		d = a + a + b;
		d1 = a + a;
		d2 = a + a + b + b;
		while (x != x1)
		{
			if (d < 0)
			{
				y += cy;
				d += d2;
			}
			else
			{
				d += d1;
			}
			x += cx;
			glBegin(GL_POINTS);
			glVertex2i(x, y);
			glEnd();
		}
	}
	else // 斜率绝对值 > 1
	{
		d = a + b + b;
		d1 = b + b;
		d2 = a + a + b + b;
		if (d < 0)
		{
			d += d1;
		}
		else
		{
			x += cx;
			d += d2;
		}
		y += cy;
		glBegin(GL_POINTS);
		glVertex2i(x, y);
		glEnd();
	}
}

运行结果:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
参考文献:
【1】 中国大学MOOC 中国农业大学 计算机图形学课程 计算机图形学
【2】 画线算法博客 - 只缘心高嫌地窄 画线算法
【3】 中点画线法(计算机图形学)- 时光足迹 中点画线法

声明:本文的主要内容来自[1], 如有违权将立即删除。

  • 21
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值