在欧氏几何空间中,平面方程就是一个三元一次方程,直线就是两个非平行平面的交线,所以直线方程就是两个三元一次方程组联立。但是在平面解析几何中,直线的方程就简单的多了。平面几何中直线方程有多种形式,一般式直线方程可用于描述所有直线:
Ax+By+C = 0 (A、B不同时为0)
当知道直线上一点坐标(X0,Y0)和直线的斜率K存在时,可以用点斜式方程:
Y-Y0 = K(X – X0) (当K不存在时,直线方程简化成X = X0)
当知道直线上的两个点(X0,Y0)和(X1,Y1)时,还可以用两点式方程描述直线:
除了这三种形式的直线方程外,直线方程还有截距式、斜截式等多种形式。
在数学范畴内的直线是由没有宽度的点组成的集合,但是在计算机图形学的范畴内,所有的图形包括直线都是输出或显示在点阵设备上的,被成为点阵图形或光栅图形。以显示器为例,现实中常见的显示器(包括CRT显示器和液晶显示器)都可以看成由各种颜色和灰度值的像素点组成的象素矩阵,这些点是有大小的,而且位置固定,因此只能近似的显示各种图形。图(1)就是对这种情况的一种夸张的放大:
图(1)直线在点阵设备上的表现形式
计算机图形学中的直线生成算法,其实包含了两层意思,一层是在解析几何空间中根据坐标构造出平面直线,另一层就是在光栅显示器之类的点阵设备上输出一个最逼近于图形的象素直线,而这就是常说的光栅图形扫描转换。本文就是介绍几种常见的直线生成的光栅扫描转换算法,包括数值微分法(DDA法)、Bresenham算法、对称直线生成算法以及两步算法。
数值微分法(DDA法)
数值微分画线算法(DDA)法是直线生成算法中最简单的一种,它是一种单步直线生成算法。它的算法是这样的:首先根据直线的斜率确定是以X方向步进还是以Y方向步进,然后沿着步进方向每步进一个点(象素),就沿另一个坐标变量k,k是直线的斜率,因为是对点阵设备输出的,所以需要对每次计算出来的一对坐标进行圆整。
具体算法的实现,除了判断是按照X方向还是按照Y方向步进之外,还要考虑直线的方向,也就是起点和终点的关系。下面就是一个支持任意直线方向的数值微分画线算法实例:
12void DDA_Line(int x1, int y1, int x2, int y2)
13{
14 double k,dx,dy,x,y,xend,yend;
15
16 dx = x2 - x1;
17 dy = y2 - y1;
18 if(fabs(dx) >= fabs(dy))
19 {
20 k = dy / dx;
21 if(dx > 0)
22 {
23 x = x1;
24 y = y1;
25 xend = x2;
26 }
27 else
28 {
29 x = x2;
30 y = y2;
31 xend = x1;
32 }
33 while(x <= xend)
34 {
35 SetDevicePixel((int)x, ROUND_INT(y));
36 y = y + k;
37 x = x + 1;
38 }
39
40 }
41 else
42 {
43 k = dx / dy;
44 if(dy > 0)
45 {
46 x = x1;
47 y = y1;
48 yend = y2;
49 }
50 else
51 {
52 x = x2;
53 y = y2;
54 yend = y1;
55 }
56 while(y <= yend)
57 {
58 SetDevicePixel(ROUND_INT(x), (int)y);
59 x = x + k;
60 y = y + 1;
61 }
62 }
63}
|
数值微分法(DDA法)产生的直线比较精确,而且逻辑简单,易于用硬件实现,但是步进量x,y和k必须用浮点数表示,每一步都要对x或y进行四舍五入后取整,不利于光栅化或点阵输出。
Bresenham算法
Bresenham算法由Bresenham在1965年提出的一种单步直线生成算法,是计算机图形学领域使用最广泛的直线扫描转换算法。Bresenham算法的基本原理就是将光栅设备的各行各列象素中心连接起来构造一组虚拟网格线。按直线从起点到终点的顺序计算直线与各垂直方向网格线的交点,然后确定该列象素中与此交点最近的象素。
图(2)直线Bresenham算法示意图
图(2)就展示了这样一组网格线,每个交点就代表点阵设备上的一个象素点,现在就以图(2)为例介绍一下Bresenham算法。当算法从一个点(Xi,Yi)沿着X方向向前步进到Xi+1时,Y方向的下一个位置只可能是Yi和Yi+1两种情况,到底是Yi还是Yi+1取决于它们与精确值y的距离d1和d2哪个更小。
d1 = y - Yi (等式 1)
d2 = Yi+1 - y (等式 2)
当d1-d2 > 0时,Y方向的下一个位置将是Yi+1,否则就是Yi。由此可见,Bresenham算法其实和数值微分算法原理是一样的,差别在于Bresenham算法中确定Y方向下一个点的位置的判断条件的计算方式不一样。现在就来分析一下这个判断条件的计算方法,已知直线的斜率k和在y轴的截距b,可推导出Xi+1位置的精确值y如下:
y = k Xi+1 + b (等式 3)
将等式 1-3带入d1-d2,可得到等式4:
的
d1-d2 = 2k Xi+1 - Yi - Yi+1 + 2b (等式 4)
有因为根据图(2)条件,k = dy / dx,Yi+1 = Yi + 1,Xi+1 =Xi + 1,将此三个关系带入等式4,同时在等式两边乘以dx,整理后可得到等式5:
dx(d1 – d2) = 2dyXi + 2dy - 2dxYi + dx(2b - 1) (等式 5)
另pi = dx(d1 – d2),则:
pi = 2dyXi + 2dy - 2dxYi + dx(2b - 1)
因为图(2)的示例dx是大于0的值,因此pi的符号与(d1 – d2)一致,现在将初始条件带入可得到最初的第一个判断条件p1:
p1 = 2dy – dx
根据Xi+1与Xi,以及Yi+1与Yi的关系,可以推出pi的递推关系:
pi+1 = pi + 2dy - 2dx(yi+1 - yi)
由于yi+1可能是yi,也可能是yi + 1,因此,pi+1就可能是以下两种可能,并且和yi的取值是对应的:
pi+1 = pi + 2dy (Y方向保持原值)
或
pi+1 = pi + 2(dy – dx) (Y方向向前步进1)
根据上面的推导,当x2 > x1,y2 > y1时Bresenham直线生成算法的计算过程如下:
1、画点(x1, y1); 计算误差初值p1=2dy-dx;
2、求直线的下一点位置:
Xi+1 = Xi+1;
如果 pi > 0 则Yi+1 = Yi + 1;
否则Yi+1 = Yi;
画点(Xi+1, Yi+1 );
3、求下一个误差pi+1;
如果 pi>0 则pi+1 = pi+2(dy – dx);
否则pi+1 = pi+2dy;
4、如果没有结束,则转到步骤2;否则结束算法。
下面就给出针对上面推导出的算法源代码(只支持 x2 > x1,y2 > y1的情况):
319void Bresenham_Line(int x1, int y1, int x2, int y2)
320{
321 int dx = abs(x2 - x1);
322 int dy = abs(y2 - y1);
323 int p = 2 * dy - dx;
324 int x = x1;
325 int y = y1;
326
327 while(x <= x2)
328 {
329 SetDevicePixel(x, y);
330 x++;
331 if(p<0)
332 p += 2 * dy;
333 else
334 {
335 p += 2 * (dy - dx);
336 y += 1;
337 }
338 }
339}
|
上面的代码只是演示计算过程,真正实用的代码要支持各种方向的直线生成,这就要考虑斜率为负值的情况以及x1 > x2的情况,另外,循环中的两次乘法运算可以在循环外计算出来,不必每次都计算。要支持各种方向的直线生成其实也很简单,就是通过坐标交换,使之符合上面演示算法的要求即可,下面就是一个实用的,支持各种方向的直线生成的Bresenham算法:
164void Bresenham_Line(int x1, int y1, int x2, int y2)
165{
166 int dx,dy,p,const1,const2,x,y,inc;
167
168 int steep = (abs(y2 - y1) > abs(x2 - x1)) ? 1 : 0;
169 if(steep == 1)
170 {
171 SwapInt(&x1, &y1);
172 SwapInt(&x2, &y2);
173 }
174 if(x1 > x2)
175 {
176 SwapInt(&x1, &x2);
177 SwapInt(&y1, &y2);
178 }
179 dx = abs(x2 - x1);
180 dy = abs(y2 - y1);
181 p = 2 * dy - dx;
182 const1 = 2 * dy;
183 const2 = 2 * (dy - dx);
184 x = x1;
185 y = y1;
186
187 inc = (y1 < y2) ? 1 : -1;
188 while(x <= x2)
189 {
190 if(steep == 1)
191 SetDevicePixel(y, x);
192 else
193 SetDevicePixel(x, y);
194 x++;
195 if(p<0)
196 p += const1;
197 else
198 {
199 p += const2;
200 y += inc;
201 }
202 }
203}
|
Bresenham算法只实用整数计算,少量的乘法运算都可以通过移位来避免,因此计算量少,效率高,
对称直线生成算法(改进的Bresenham算法)
直线段有个特性,那就是直线段相对于中心点是两边对称的。因此可以利用这个对称性,对其它单步直线生成算法进行改进,使得每进行一次判断或相关计算可以生成相对于直线中点的两个对称点。如此以来,直线就由两端向中间生成。从理论上讲,这个改进可以应用于任何一种单步直线生成算法,本例就只是对Bresenham算法进行改进。
改进主要集中在以下几点,首先是循环区间,由[x1, x2]修改成[x1, half],half是区间[x1, x2]的中点。其次是X轴的步进方向改成双向,最后是Y方向的值要对称修改,除此之外,算法整体结构不变,下面就是改进后的代码:
205void Sym_Bresenham_Line(int x1, int y1, int x2, int y2)
206{
207 int dx,dy,p,const1,const2,xs,ys,xe,ye,half,inc;
208
209 int steep = (abs(y2 - y1) > abs(x2 - x1)) ? 1 : 0;
210 if(steep == 1)
211 {
212 SwapInt(&x1, &y1);
213 SwapInt(&x2, &y2);
214 }
215 if(x1 > x2)
216 {
217 SwapInt(&x1, &x2);
218 SwapInt(&y1, &y2);
219 }
220 dx = x2 - x1;
221 dy = abs(y2 - y1);
222 p = 2 * dy - dx;
223 const1 = 2 * dy;
224 const2 = 2 * (dy - dx);
225 xs = x1;
226 ys = y1;
227 xe = x2;
228 ye = y2;
229 half = (dx + 1) / 2;
230 inc = (y1 < y2) ? 1 : -1;
231 while(xs <= half)
232 {
233 if(steep == 1)
234 {
235 SetDevicePixel(ys, xs);
236 SetDevicePixel(ye, xe);
237 }
238 else
239 {
240 SetDevicePixel(xs, ys);
241 SetDevicePixel(xe, ye);
242 }
243 xs++;
244 xe--;
245 if(p<0)
246 p += const1;
247 else
248 {
249 p += const2;
250 ys += inc;
251 ye -= inc;
252 }
253 }
254}
|
两步算法
两步算法是在生成直线的过程中,每次判断都生成两个点的直线生成算法。上一节介绍的对称直线生成方法也是每次生成两个点,但是它和两步算法的区别就是对称方法的计算和判断是从线段的两端向中点进行,而两步算法是沿着一个方向,一次生成两个点。
当斜率k满足条件0≤k<1时,假如当前点P已经确定,如图(3)所示,则P之后的连续两个点只可能是四种情况:AB,AC,DC和DE,两步算法设立决策量e作为判断标志,e的初始值是4dy – dx,其中:
dy = y2 – y1
dx = x2 – x1。
图(3)直线两步算法示意图
为简单起见,先考虑dy > dx > 0这种情况。当e > 2dx时,P后两个点将会是DE组合,此时e的增量是4dy – 4dx。当dx < e < 2dx时,P后的两个点将会是DC组合,此时e的增量是4dy – 2dx.。当0 < e < dx时,P后的两个点将会是AC组合,此时e的增量是4dy – 2dx.。当e < 0时,P后的两个点将会是AB组合,此时e的增量是4dy。综合以上描述,当斜率k满足条件0≤k<1,且dy > dx > 0这种情况下,两步算法可以这样实现:
257void Double_Step_Line(int x1, int y1, int x2, int y2)
258{
259 int dx = x2 - x1;
260 int dy = y2 - y1;
261 int e = dy * 4 - dx;
262 int x = x1;
263 int y = y1;
264
265 SetDevicePixel(x, y);
266
267 while(x < x2)
268 {
269 if (e > dx)
270 {
271 if (e > ( 2 * dx))
272 {
273 e += 4 * (dy - dx);
274 x++;
275 y++;
276 SetDevicePixel(x, y);
277 x++;
278 y++;
279 SetDevicePixel(x, y);
280 }
281 else
282 {
283 e += (4 *dy - 2 * dx);
284 x++;
285 y++;
286 SetDevicePixel(x, y);
287 x++;
288 SetDevicePixel(x, y);
289 }
290 }
291 else
292 {
293 if (e > 0)
294 {
295 e += (4 * dy - 2 * dx);
296 x++;
297 SetDevicePixel(x, y);
298 x++;
299 y++;
300 SetDevicePixel(x, y);
301 }
302 else
303 {
304 x++;
305 SetDevicePixel(x, y);
306 x++;
307 SetDevicePixel(x, y);
308 e += 4 * dy;
309 }
310 }
311 }
312}
|
以上函数除了只支持一个方向的直线生成之外,还有其它不完善的地方,比如没有判断最后一个点是否会越界,大量出现的乘法计算可以用移位处理等等。仿照Bresenham算法一节介绍的方法,很容易将其扩展为支持8个方向的直线生成,因为代码比较长,这里就不列出代码了。
总结
除了以上介绍的几种直线生成算法,还有很多其它的直线光栅扫描转换算法,比如三步算法、四步算法、中点划线法等等,还有人将三步算法结合前面介绍的对称法提出了一种可以一次画六个点的直线生成算法,这里就不多介绍了,有兴趣的读者可以找计算机图形学的相关资料来了解具体的内容。
本文介绍的几种直线生成算法中,DDA算法最简单,但是因为有多次浮点数乘法和除法运算,以及浮点数圆整运算,效率比较低。Bresenham算法中的整数乘法计算都可以用移位代替,主要运算都采用了整数加法和减法运算,因此效率比较高,各种各样变形的Bresenham算法在计算机图形软件中得到了广泛的应用。理论上讲,两步算法以及四步算法效率应该更高一些,但是这两种算法需要做比较多的准备工作,且多是乘法和除法运算,因此在生成比较短的直线时,效率反而不如Bresenham算法。
参考资料:
【1】计算几何:算法设计与分析 周培德 清华大学出版社 2005年
【2】计算几何:算法与应用 德贝尔赫(邓俊辉译) 清华大学出版社 2005年
【3】计算机图形学 孙家广、杨常贵 清华大学出版社 1995年
分享到:
相关推荐
Bresenham直线生成算法是目前最常用的一种直线绘制算法,由Jack E. Bresenham于1965年提出。它基于误差累积的概念,以最小的计算量和内存需求来近似绘制直线。算法的主要步骤如下: 1. 初始化两个变量,x_error表示x...
### 直线生成算法 1. **DDA(Digital Differential Analyzer)算法**:是最简单的直线生成算法。它通过将直线的起点和终点坐标转换为整数,并以恒定的步长沿着x轴和y轴进行递增,直到达到终点。这种方法计算简单,...
5. 直线生成算法:直线生成算法是计算机图形学中的基本算法,包括数值微分法(DDA)、中点画线算法和Bresenham画线算法。这些算法可以生成直线、圆和其他图形。 6. 中点画线算法:中点画线算法是直线生成算法的一种...
### 直线生成算法实验报告知识点解析 #### 实验背景与目标 本次实验的主要目的是让学生通过实际编程操作,深入理解并熟练掌握直线的生成算法,尤其是Bresenham直线生成算法。通过实验,学生需要掌握以下技能: 1....
在计算机图形学中,直线生成算法是至关重要的一个部分,用于在屏幕上绘制直线。本项目主要探讨了“直线生成算法”,并且使用C++编程语言(VC++环境)实现了其中的中点画线算法(Midpoint Drawing Algorithm),这也...
有多种算法可以实现直线的绘制,本文将详细介绍三种常用的直线算法:DDA算法、中点算法和Bresenham算法。 DDA算法 DDA(Digital Differential Analyzer)算法是一种简单的直线算法,它基于直线的斜率来确定每个...
在计算机图形学中,直线生成算法是基础且至关重要的,它使得计算机能够精确地描绘出直线,为复杂的图形渲染和图像处理提供基础。在Windows编程环境中,VC++(Visual C++)常被用于开发图形应用,因此理解并掌握直线...
"计算机图形学-三种直线生成算法及圆的生成算法" 计算机图形学是计算机科学与技术学院中的一个重要方向,它涉及到计算机图形的生成、显示和处理。在计算机图形学中,直线和圆是最基本的图形元素,本文将介绍三种...
2. 掌握几种基本的直线生成算法:DDA 算法、Bresenham 画线法、中点画线法 3. 实现直线生成的,在屏幕上任意生成一条直线 三、实现程序: 本实验使用 C 语言编写,使用 Borland Graphics Interface(BGI)库来实现...
### DDA直线生成算法及其C语言实现 #### DDA算法简介 数字差分分析器(Digital Differential Analyzer,简称DDA)是一种用于计算机图形学中的基本绘图算法之一,主要用于生成直线段。它通过逐步累加斜率的方式计算...
实验的主要目标是掌握3种常见的直线生成算法:数值微分(DDA)算法、中点算法以及Bresenham算法,并能够在MFC环境下正确绘制直线。 **1. DDA(Digital Differential Analyzer)算法** DDA算法基于直线方程y = kx +...
VC 6.0 演示一种直线生成的算法,演示了三种直线生成算法 :1、中点算法生成直线;2、bresenham算法生成直线;3、DDA算法 生成直线。在运行的实例窗口中,单击“直线生成算法”菜单,可通过弹出的菜单,选择不同的...
### DDA直线生成算法 #### 一、简介 在计算机图形学中,DDA(Digital Differential Analyzer,数字差分分析器)算法是一种用于绘制直线的基本算法。它通过计算每一步移动的方向来逼近理想直线的位置,从而实现直线...
下面将详细讨论几种常见的直线生成算法。 1. **扫描线算法**: 扫描线算法是一种基本的二维图形绘制方法,适用于屏幕是由一系列水平行像素组成的场景。该算法从屏幕的顶部到底部逐行扫描,检查每条直线是否与当前...
02DDA:直线的DDA生成算法 03MidPaint:直线中点生成算法 04Bresenham:直线Bresenham生成算法 05DrawCircle:中点画圆算法 06DrawEllipse:中点画椭圆算法 07EdgeTablePolygon:多边形有序边表算法 08:边标志...
在计算机图形学中,直线段的生成算法是基础且重要的组成部分。这些算法使得计算机能够高效地在屏幕上绘制出平滑的线条。以下是三种常见的直线画法:DDA算法、中点画线算法以及Bresenham算法。 1. DDA(Digital ...
其中,数字微分分析器(Digital Differential Analyzer,简称DDA)算法是一种常用的线性插值方法,用于生成直线上的像素点。本文将深入探讨DDA算法的工作原理及其在实际编程中的应用。 ### DDA算法原理 DDA算法的...
选定一种基本图形(直线段),编写生成该基本图形的源程序,并能在计算机上编译运行,画出正确的图形
计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机图形学 直线DDA算法计算机...
在计算机图形学领域,直线生成算法是至关重要的基础部分,特别是在使用编程语言如VB(Visual Basic)进行图形绘制时。直线生成算法允许我们通过代码在屏幕上精确地描绘出直线,这是构建更复杂的图形和图像的基础。...