圆和椭圆的画线算法
一、圆的画线算法
- 圆的中点画线算法
原理:圆的中点画线算法主要是因为圆具有八对称的特性,因此只需要完成八分之一圆弧,然后将八分之一圆弧上的点以X,Y轴和Y=X这条直线进行对称操作。所以在此算法中,我们只需要对斜率k在[0,1]之间的八分之圆弧进行分析。
过程:构造判别式
判断中点M与圆的位置关系,判别式的值<0在圆内,>0在圆外,=0在圆上
设d=F(M)=F(xp+1,yp-0.5)=(xp+1)2 +(yp-0.5)2 - r2
当d<0时,M在圆内,取P1作为下一像素点;
当d>0时,M在园外,取P2作为下一像素点;
当d=0时,M在圆上,取任一点即可,这里我们取P2。
确定了取点之后我们需要计算下一像素点的判别式d’:
如果d<0,d’=F(xp+2,yp-0.5)=d+2xp+3
反之d>=0,d’=F(xp+2,yp-1.5)=d+2(xp-yp)+5
因为第一个像素点为(0,r),d的初始值为1.25-r
核心代码如下:
//中点画圆算法
void CircleDraw_DDA() {
int x, y;
double d;
x = 380;
y = 380 + r;
d = 12.5 - r;
double x1 = x, y1, r1 = r;
//描线
while ((x1 - 380) <= r1) {
y1 = sqrt(r1 * r1 - (x1 - 380) * (x1 - 380));
line(x1, 380 + y1, x1, 380 + y1, RGB(0, 255, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(0, 255, 0));
x1 += 0.1;
}
while ((-x1 + 380) <= r1) {
y1 = sqrt(r1 * r1 - (x1 - 380) * (x1 - 380));
line(x1, 380 + y1, x1, 380 + y1, RGB(0, 255, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(0, 255, 0));
x1 -= 0.1;
}
//描点
while (x <= y) {
line(x - 2, y - 2, x + 2, y - 2, RGB(255, 0, 0));
line(x - 2, y - 2, x - 2, y + 2, RGB(255, 0, 0));
line(x - 2, y + 2, x + 2, y + 2, RGB(255, 0, 0));
line(x + 2, y - 2, x + 2, y + 2, RGB(255, 0, 0));
line(y - 2, x - 2, y + 2, x - 2, RGB(255, 0, 0));
line(y - 2, x - 2, y - 2, x + 2, RGB(255, 0, 0));
line(y - 2, x + 2, y + 2, x + 2, RGB(255, 0, 0));
line(y + 2, x - 2, y + 2, x + 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x + 2, 760 - y - 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x - 2, 760 - y + 2, RGB(255, 0, 0));
line(x - 2, 760 - y + 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(x + 2, 760 - y - 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(y - 2, 760 - x - 2, y + 2, 760 - x - 2, RGB(255, 0, 0));
line(y - 2, 760 - x - 2, y - 2, 760 - x + 2, RGB(255, 0, 0));
line(y - 2, 760 - x + 2, y + 2, 760 - x + 2, RGB(255, 0, 0));
line(y + 2, 760 - x - 2, y + 2, 760 - x + 2, RGB(255, 0, 0));
line(760 - x - 2, y - 2, 760 - x + 2, y - 2, RGB(255, 0, 0));
line(760 - x - 2, y - 2, 760 - x - 2, y + 2, RGB(255, 0, 0));
line(760 - x - 2, y + 2, 760 - x + 2, y + 2, RGB(255, 0, 0));
line(760 - x + 2, y - 2, 760 - x + 2, y + 2, RGB(255, 0, 0));
line(760 - y - 2, x - 2, 760 - y + 2, x - 2, RGB(255, 0, 0));
line(760 - y - 2, x - 2, 760 - y - 2, x + 2, RGB(255, 0, 0));
line(760 - y - 2, x + 2, 760 - y + 2, x + 2, RGB(255, 0, 0));
line(760 - y + 2, x - 2, 760 - y + 2, x + 2, RGB(255, 0, 0));
line(760 - x - 2, 760 - y - 2, 760 - x + 2, 760 - y - 2, RGB(255, 0, 0));
line(760 - x - 2, 760 - y - 2, 760 - x - 2, 760 - y + 2, RGB(255, 0, 0));
line(760 - x - 2, 760 - y + 2, 760 - x + 2, 760 - y + 2, RGB(255, 0, 0));
line(760 - x + 2, 760 - y - 2, 760 - x + 2, 760 - y + 2, RGB(255, 0, 0));
line(760 - y - 2, 760 - x - 2, 760 - y + 2, 760 - x - 2, RGB(255, 0, 0));
line(760 - y - 2, 760 - x - 2, 760 - y - 2, 760 - x + 2, RGB(255, 0, 0));
line(760 - y - 2, 760 - x + 2, 760 - y + 2, 760 - x + 2, RGB(255, 0, 0));
line(760 - y + 2, 760 - x - 2, 760 - y + 2, 760 - x + 2, RGB(255, 0, 0));
if (d < 0) {
d += 2 * (x - 380) + 30;
}
else {
d += 2 * ((x - 380) - (y - 380)) + 50;
y -= 10;
}
x += 10;
nanosleep((const struct timespec[]) { {0, 100000000L} }, NULL); //每一次描点完成后延迟
}
}
注:此代码编译环境为Qemu
- 圆的Bresenham画线算法
原理:Bresenham与中点画线算法的不同在于前者可直接利用四对称画圆,而后者需要用到八对称,Bresenham算法将H,D,V三点到圆的的距离分别做减法。
过程:构造函数:F(x,y)=x2 +y2 -r2
Hi=(xi+1)2 +yi2 -r2
Di=(xi+1)2 +(yi-1)2 -r2
Vi=xi2 +(yi-1)2 -r2
令d1=|Hi|-|Di|=2*(Di+yi)-1
d2=|D|-|V|=2*(Di-xi)-1
当Di<0时,若d1<0,取H点,反之取D点
当Di>0时,若d2<0,取D点,反之取V点
当Di=0时,取D点
确定取点后,需要确定下一像素点的递推公式如果取点为H点:D(i+1)=Di+2x(i+1)+1
如果取点为D点:D(i+1)=Di+2x(i+1)-2y(i+1)+2
如果取点为V点:D(i+1)=Di-2*y(i+1)+1
核心代码如下:
//Bresenham画圆算法
void CircleDraw_Bresenham() {
int x = 380, y = 380 + r;
double x1 = x, y1, r1 = r;
int delta=2*(1-r/10), delta1, delta2, direction;
while ((x1 - 380) <= r1) {
y1 = sqrt(r1 * r1 - (x1 - 380) * (x1 - 380));
line(x1, 380 + y1, x1, 380 + y1, RGB(255, 0, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(255, 0, 0));
x1 += 0.01;
}
while ((-x1 + 380) <= r1) {
y1 = sqrt(r1 * r1 - (x1 - 380) * (x1 - 380));
line(x1, 380 + y1, x1, 380 + y1, RGB(255, 0, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(255, 0, 0));
x1 -= 0.01;
}
while (y >= 380) {
line(x - 2, y - 2, x + 2, y - 2, RGB(0, 255, 0));
line(x - 2, y - 2, x - 2, y + 2, RGB(0, 255, 0));
line(x - 2, y + 2, x + 2, y + 2, RGB(0, 255, 0));
line(x + 2, y - 2, x + 2, y + 2, RGB(0, 255, 0));
line(x - 2, 760 - y - 2, x + 2, 760 - y - 2, RGB(0, 255, 0));
line(x - 2, 760 - y - 2, x - 2, 760 - y + 2, RGB(0, 255, 0));
line(x - 2, 760 - y + 2, x + 2, 760 - y + 2, RGB(0, 255, 0));
line(x + 2, 760 - y - 2, x + 2, 760 - y + 2, RGB(0, 255, 0));
line(760 - x - 2, y - 2, 760 - x + 2, y - 2, RGB(0, 255, 0));
line(760 - x - 2, y - 2, 760 - x - 2, y + 2, RGB(0, 255, 0));
line(760 - x - 2, y + 2, 760 - x + 2, y + 2, RGB(0, 255, 0));
line(760 - x + 2, y - 2, 760 - x + 2, y + 2, RGB(0, 255, 0));
line(760 - x - 2, 760 - y - 2, 760 - x + 2, 760 - y - 2, RGB(0, 255, 0));
line(760 - x - 2, 760 - y - 2, 760 - x - 2, 760 - y + 2, RGB(0, 255, 0));
line(760 - x - 2, 760 - y + 2, 760 - x + 2, 760 - y + 2, RGB(0, 255, 0));
line(760 - x + 2, 760 - y - 2, 760 - x + 2, 760 - y + 2, RGB(0, 255, 0));
if (delta < 0) {
delta1 = 2 * (delta + (y-380) / 10) - 1; //HD距离大小
if (delta1 <= 0)
direction = 1;
else direction = 2;
}
else if (delta > 0) {
delta2 = 2 * (delta - (x-380) / 10) - 1; //DV距离大小
if (delta2 < 0)
direction = 2;
else direction = 3;
}
else
direction = 2;
switch (direction) {
case 1: //取H点
x += 10;
delta += 2 * ((x-380) / 10) + 1;
break;
case 2: //取D点
x += 10;
y -= 10;
delta += 2 * ((x-380) / 10 - (y-380) / 10 + 1);
break;
case 3: //取V点
y -= 10;
delta += (-2) * ((y-380) / 10) + 1;
break;
}
nanosleep((const struct timespec[]) { {0, 100000000L} }, NULL);
}
}
注:此代码编译环境为Qemu
二、椭圆画线算法
- 椭圆的Bresenham画线算法
原理:由于椭圆的四对称性,考虑第一象限圆弧生成即可,并以切线斜率为-1的点作为分界点。
过程:构造函数:F(x,y)=b2x2 +a2y2 -a2b2=0
先对椭圆的上部分进行分析,中点判别式d1=F(xp+1,yp-0.5)=b2(xp+1)2 +a2(yp-0.5)2 -a2b2
当d1<0,中点在椭圆内,取正右方的像素点,新判别式d1’=d1+b2(2xp+3)
当d1>=0,中点在椭圆外,取右下方像素点,新判别式为d1’=b2(2xp+3)+a2(-2yp+2)
d1的初始值为:b2 +a2(-b+0.25)
接着分析椭圆的下半部分,上半部分到下半部分的判断条件为b2x=-a2y
当b2(xi+1)<a2(yi-0.5)时,处于椭圆的上半部分
当b2(xi+1)>a2(yi-0.5),椭圆由上半部分进入下半部分
下半部分的判别式d2=F(xp+0.5,yp-1)=b2(xp+0.5)2 +a2(yp-1)2 -a2b2
如果d2>0,取(xp,yp-1),新判别式d2’=d2+a2(-2yp+3)
如果d2<=0,取(xp+1,yp-1),新判别式d2’=d2+b2(2xp+2)+a2(-2yp+3)
d2的初始值为b2(x+0.5)2 +a2(y-1)2 -a2b2
核心代码如下:
//椭圆画线算法
void EllipseDraw() {
int x = 500, y = 380 + b;
int a1 = a / 10, b1 = b / 10;
double d = b1 * b1 + a1 * a1 * (-b1 + 0.25);
double x1 = x;
double y1;
while ((x1 - 500) <= a) {
y1 = sqrt(b * b * (1 - ((x1 - 500) * (x1 - 500) / (a * a))));
line(x1, 380 + y1, x1, 380 + y1, RGB(0, 255, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(0, 255, 0));
x1 += 0.1;
}
while ((-x1 + 500) <= a) {
y1 = sqrt(b * b * (1 - (x1 - 500) * (x1 - 500) / (a * a)));
line(x1, 380 + y1, x1, 380 + y1, RGB(0, 255, 0));
line(x1, 380 - y1, x1, 380 - y1, RGB(0, 255, 0));
x1 -= 0.1;
}
while (y>=380) {
if (b1 * b1 * ((x - 500) / 10 + 1) < a1 * a1 * ((y - 380) / 10 - 0.5)) {
line(x - 2, y - 2, x + 2, y - 2, RGB(255, 0, 0));
line(x - 2, y - 2, x - 2, y + 2, RGB(255, 0, 0));
line(x - 2, y + 2, x + 2, y + 2, RGB(255, 0, 0));
line(x + 2, y - 2, x + 2, y + 2, RGB(255, 0, 0));
line(1000 - x - 2, y - 2, 1000 - x + 2, y - 2, RGB(255, 0, 0));
line(1000 - x - 2, y - 2, 1000 - x - 2, y + 2, RGB(255, 0, 0));
line(1000 - x - 2, y + 2, 1000 - x + 2, y + 2, RGB(255, 0, 0));
line(1000 - x + 2, y - 2, 1000 - x + 2, y + 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x + 2, 760 - y - 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x - 2, 760 - y + 2, RGB(255, 0, 0));
line(x - 2, 760 - y + 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(x + 2, 760 - y - 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y - 2, 1000 - x + 2, 760 - y - 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y - 2, 1000 - x - 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y + 2, 1000 - x + 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x + 2, 760 - y - 2, 1000 - x + 2, 760 - y + 2, RGB(255, 0, 0));
if (d <= 0) {
d += b1 * b1 * (2 * ((x - 500) / 10) + 3);
x += 10;
}
else {
d += b1 * b1 * (2 * ((x - 500) / 10) + 3) + a1 * a1 * ((-2) * ((y - 380) / 10) + 2);
x += 10;
y -= 10;
}
nanosleep((const struct timespec[]) { {0, 100000000L} }, NULL);
}
else {
double d1 = b1 * b1 * ((x - 500) / 10 + 0.5) * ((x - 500) / 10 + 0.5) + a1 * a1 * ((y - 380) / 10 - 1) * ((y - 380) / 10 - 1) - a1 * a1 * b1 * b1;
while (y >= 380) {
line(x - 2, y - 2, x + 2, y - 2, RGB(255, 0, 0));
line(x - 2, y - 2, x - 2, y + 2, RGB(255, 0, 0));
line(x - 2, y + 2, x + 2, y + 2, RGB(255, 0, 0));
line(x + 2, y - 2, x + 2, y + 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x + 2, 760 - y - 2, RGB(255, 0, 0));
line(x - 2, 760 - y - 2, x - 2, 760 - y + 2, RGB(255, 0, 0));
line(x - 2, 760 - y + 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(x + 2, 760 - y - 2, x + 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x - 2, y - 2, 1000 - x + 2, y - 2, RGB(255, 0, 0));
line(1000 - x - 2, y - 2, 1000 - x - 2, y + 2, RGB(255, 0, 0));
line(1000 - x - 2, y + 2, 1000 - x + 2, y + 2, RGB(255, 0, 0));
line(1000 - x + 2, y - 2, 1000 - x + 2, y + 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y - 2, 1000 - x + 2, 760 - y - 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y - 2, 1000 - x - 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x - 2, 760 - y + 2, 1000 - x + 2, 760 - y + 2, RGB(255, 0, 0));
line(1000 - x + 2, 760 - y - 2, 1000 - x + 2, 760 - y + 2, RGB(255, 0, 0));
if (d1 <= 0) {
d1 = d1 + b1 * b1 * (2 * (x - 500) / 10 + 2) + a1 * a1 * ((-2) * ((y - 380) / 10) + 3);
x += 10;
y -= 10;
}
else {
d1 = d1 + a1 * a1 * ((-2) * (y - 380) / 10 + 3);
y -= 10;
}
nanosleep((const struct timespec[]) { {0, 100000000L} }, NULL);
}
}
}
}
注:此代码编译环境为Qemu