计算机图形学

圆和椭圆的画线算法

一、圆的画线算法

  1. 圆的中点画线算法
    原理:圆的中点画线算法主要是因为圆具有八对称的特性,因此只需要完成八分之一圆弧,然后将八分之一圆弧上的点以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

  1. 圆的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+2
    x(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值