Bresenham直线算法与画圆算法
在我们内部开发使用的一个工具中,我们需要几乎从 0 开始实现一个高效的二维图像渲染引擎。比较幸运的是,我们只需要画直线、圆以及矩形,其中比较复杂的是画直线和圆。画直线和圆已经有非常多的成熟的算法了,我们用的是Bresenham的算法。
计算机是如何画直线的?简单来说,如下图所示,真实的直线是连续的,但我们的计算机显示的精度有限,不可能真正显示连续的直线,于是我们用一系列离散化后的点(像素)来近似表现这条直线。
(上图来自于互联网络,《计算机图形学的概念与方法》柳朝阳,郑州大学数学系)
接下来的问题就是如何尽可能高效地找到这些离散的点,Bresenham直线算法就是一个非常不错的算法。
Bresenham直线算法是用来描绘由两点所决定的直线的算法,它会算出一条线段在 n 维光栅上最接近的点。这个算法只会用到较为快速的整数加法、减法和位元移位,常用于绘制电脑画面中的直线。是计算机图形学中最先发展出来的算法。
(引自wiki百科布雷森漢姆直線演算法)
这个算法的流程图如下:
可以看到,算法其实只考虑了斜率在 0 ~ 1 之间的直线,也就是与 x 轴夹角在 0 度到 45 度的直线。只要解决了这类直线的画法,其它角度的直线的绘制全部可以通过简单的坐标变换来实现。
下面是一个C语言实现版本。
// 交换整数 a 、b 的值
inline void swap_int(int *a, int *b) {
*a ^= *b;
*b ^= *a;
*a ^= *b;
} // Bresenham's line algorithm
void draw_line(IMAGE *img, int x1, int y1, int x2, int y2, unsigned long c) {
// 参数 c 为颜色值
int dx = abs(x2 - x1),
dy = abs(y2 - y1),
yy = ; if (dx < dy) {
yy = ;
swap_int(&x1, &y1);
swap_int(&x2, &y2);
swap_int(&dx, &dy);
} int ix = (x2 - x1) > ? : -,
iy = (y2 - y1) > ? : -,
cx = x1,
cy = y1,
n2dy = dy * ,
n2dydx = (dy - dx) * ,
d = dy * - dx; if (yy) { // 如果直线与 x 轴的夹角大于 45 度
while (cx != x2) {
if (d < ) {
d += n2dy;
} else {
cy += iy;
d += n2dydx;
}
putpixel(img, cy, cx, c);
cx += ix;
}
} else { // 如果直线与 x 轴的夹角小于 45 度
while (cx != x2) {
if (d < ) {
d += n2dy;
} else {
cy += iy;
d += n2dydx;
}
putpixel(img, cx, cy, c);
cx += ix;
}
}
}
可以看到,在画线的循环中,这个算法只用到了整数的加法,所以可以非常的高效。
接下来,我们再来看一看Bresenham画圆算法。
Bresenham画圆算法又称中点画圆算法,与Bresenham 直线算法一样,其基本的方法是利用判别变量来判断选择最近的像素点,判别变量的数值仅仅用一些加、减和移位运算就可以计算出来。为了简便起见,考虑一个圆 心在坐标原点的圆,而且只计算八分圆周上的点,其余圆周上的点利用对称性就可得到。
为什么只计算八分圆周上的点就可以了呢?和上面的直线算法类似,圆也有一个“八对称性”,如下图所示。
显然,我们只需要知道了圆上的一个点的坐标 (x, y) ,利用八对称性,我们马上就能得到另外七个对称点的坐标。
和直线算法类似,Bresenham画圆算法也是用一系列离散的点来近似描述一个圆,如下图。
(上图来自于互联网络,《计算机图形学的概念与方法》柳朝阳,郑州大学数学系)
Bresenham画圆算法的流程图如下。
可以看到,与画线算法相比,画圆的循环中用到了整数的乘法,相对复杂了一些。
下面是一个C语言实现版本。
// 八对称性
inline void _draw_circle_8(IMAGE *img, int xc, int yc, int x, int y, unsigned long c) {
// 参数 c 为颜色值
putpixel(img, xc + x, yc + y, c);
putpixel(img, xc - x, yc + y, c);
putpixel(img, xc + x, yc - y, c);
putpixel(img, xc - x, yc - y, c);
putpixel(img, xc + y, yc + x, c);
putpixel(img, xc - y, yc + x, c);
putpixel(img, xc + y, yc - x, c);
putpixel(img, xc - y, yc - x, c);
} //Bresenham's circle algorithm
void draw_circle(IMAGE *img, int xc, int yc, int r, int fill, unsigned long c) {
// (xc, yc) 为圆心,r 为半径
// fill 为是否填充
// c 为颜色值 // 如果圆在图片可见区域外,直接退出
if (xc + r < || xc - r >= img->w ||
yc + r < || yc - r >= img->h) return; int x = , y = r, yi, d;
d = - * r; if (fill) {
// 如果填充(画实心圆)
while (x <= y) {
for (yi = x; yi <= y; yi ++)
_draw_circle_8(img, xc, yc, x, yi, c); if (d < ) {
d = d + * x + ;
} else {
d = d + * (x - y) + ;
y --;
}
x++;
}
} else {
// 如果不填充(画空心圆)
while (x <= y) {
_draw_circle_8(img, xc, yc, x, y, c); if (d < ) {
d = d + * x + ;
} else {
d = d + * (x - y) + ;
y --;
}
x ++;
}
}
}
可以看到,Bresenham画圆算法(中点圆算法)的实现也非常简单。在另一个项目中,我还有一个 Python 实现的版本,不过那段代码和其余部分结合得比较紧,有点难抠,这次就不贴出来了。
Bresenham直线算法与画圆算法的更多相关文章
- 中点Brehensam画圆算法
#include<stdio.h> #include<stdlib.h> #include<graphics.h> #include<math.h> v ...
- 基于Bresenham算法画圆
bresenham算法画圆思想与上篇 bresenham算法画线段 思想是一致的 画圆x^2+y^2=R^2 将他分为8个部分,如上图 1. 只要画出1中1/8圆的圆周,剩下的就可以通过对称关系画出这 ...
- 中点Bresenham画圆
这里不仔细讲原理,只是把我写的算法发出来,跟大家分享下,如果有错误的话,还请大家告诉我,如果写的不好,也请指出来,一起讨论进步. 算法步骤: (1) 输入圆的半径R. (2) 计算初始值d = 1 - ...
- 《图形学》实验六:中点Bresenham算法画圆
开发环境: VC++6.0,OpenGL 实验内容: 使用中点Bresenham算法画圆. 实验结果: 代码: #include <gl/glut.h> #define WIDTH 500 ...
- [计算机图形学] 基于C#窗口的Bresenham直线扫描算法、种子填充法、扫描线填充法模拟软件设计(二)
上一节链接:http://www.cnblogs.com/zjutlitao/p/4116783.html 前言: 在上一节中我们已经大致介绍了该软件的是什么.可以干什么以及界面的大致样子.此外还详细 ...
- 【转】【OPenGL】OPenGL 画图板-- 中点算法画圆
为了能以任意点为圆心画圆,我们可以把圆心先设为视点(相当于于将其平移到坐标原点),然后通过中点法扫描转换后,再恢复原来的视点(相当于将圆心平移回原来的位置). 圆心位于原点的圆有四条对称轴x=0,y= ...
- Bresenham画线算法
[Bresenham画线算法] Bresenham是一种光栅化算法.不仅可以用于画线,也可以用用画圆及其它曲线. 通过lower与upper的差,可以知道哪一个点更接近线段: 参考:<计算机图形 ...
- WebGIS中基于AGS的画圆查询简析以及通过Polygon来构造圆的算法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 某个项目需求中需要在前端进行画圆查询,将圆范围上的多边形要素 ...
- 两种画线算法(DDA&Bersenham)
DDA(digital differential analyzer) 由直线的斜截式方程引入 对于正斜率的线段,如果斜率<=1,则以单位x间隔(δx=1)取样,并逐个计算每一个y值 Yk+1 = ...
随机推荐
- MySQL复合主键下ON DUPLICATE KEY UPDATE语句失效问题
问题的起因,假设有一张表,里面保存了交易订单,每张订单有唯一的ID,有最后更新时间,还有数据,详情如下: 1 2 3 4 5 6 7 +-------+----------+------+-----+ ...
- UVAlive4287_Proving Equivalences
题意是告诉你有n个命题,m条递推关系,表示某个命题可以推出另外一个命题. 现在问你至少在增加多少个递推关系可以保证所有命题两两互推. 命题为点,关系为有向边,题目转化成为至少增加多少条有向边使得整个图 ...
- Python学习---列表,元组,字典
### 列表 list = [1,2,3,4,5,6] list.append(7) print(list) ===>>> [1, 2, 3, 4, 5, 6, 7] list[2] ...
- P2605 [ZJOI2010]基站选址
题目描述 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范 ...
- 浅谈kmp
简介: 一种由Knuth(D.E.Knuth).Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法.这个算法不用计算变迁函数δ,匹配时间为Θ(n), ...
- BZOJ 3190 赛车 | 计算几何
BZOJ 3190 赛车 题面 这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2--gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进ki的位置.比赛开始后,车辆gi将会以 ...
- USACO Section 2.1 Ordered Fractions 解题报告
题目 题目描述 给定一个数N(1<=N<=160),需要产生所有的分数,这些分数的值必须要在0~1之间.而且每个分数的分母不能超过N.如下例所示: N = 5 产生所有的分数:0/1 1/ ...
- CJOJ 2482 【POI2000】促销活动(STL优先队列,大根堆,小根堆)
CJOJ 2482 [POI2000]促销活动(STL优先队列,大根堆,小根堆) Description 促销活动遵守以下规则: 一个消费者 -- 想参加促销活动的消费者,在账单下记下他自己所付的费用 ...
- my-innodb-heavy-4G.cnf配置文件注解
[client] ####客户端 port = 3306 ####mysql客户端连接时的默认端口号 socket = /application/mysql-5.5.32/tmp/mysql.sock ...
- ElasticStack系列之十四 & ElasticSearch5.x bulk update 中重复 id 性能骤降
目前在绝对多数公司在使用 ElasticSearch 将其当做数据库使用,将多个数据库中的数据同步到 ElasticSearch 索引是非常常见的应用场景.那么自然而然就会涉及到数据频繁的新增和更新, ...