> 文章列表 > 【计算机图形学】扫描转换算法(Bresenham1/4圆法 椭圆两头逼近法 方形刷子)

【计算机图形学】扫描转换算法(Bresenham1/4圆法 椭圆两头逼近法 方形刷子)

【计算机图形学】扫描转换算法(Bresenham1/4圆法  椭圆两头逼近法  方形刷子)

一 实验目的

  1. 编写弧线的光栅扫描转换算法,并对线宽与线形的算法加以探讨
  2. 熟悉圆和椭圆画线的算法

二 实验算法理论分析
Bresenham法(1/4圆)


椭圆扫描转换——两头逼近法:

处理线宽问题:

 方形刷子宽度存在的问题:

1:线段的始末端点总是水平或垂直的。

2:水平或垂直方形刷子与线条垂直,其粗细与线宽相等,而对于45°斜线,粗细为指定线宽的1.4倍。

3:重复写象素。为每条扫描线建一个活化边表,存放该扫描线与线条的相交区间左右端点位置。

三 实验内容

4:用四分法画半径为r的一个完整的圆,用鼠标选择圆心位置。

实验结果如下图所示:
第一步:输入所画圆的半径和勾勒的颜色(此处以50像素为圆的半径,白色为勾勒的颜色为例)。


第二步:通过鼠标左键点击画布,编译后的文件自动以当前的鼠标坐标为圆心,画出目标半径和颜色的圆形图案。

5:画一个半长轴为a和半短轴为b的完整椭圆,用鼠标选择圆心位置。

实验结果如下图所示:
第一步:输入所画椭圆的半长轴、半短轴和勾勒的颜色(此处以200像素和100像素为椭圆的半长轴和半短轴,白色为勾勒的颜色为例)。


第二步:通过鼠标左键点击画布,编译后的文件自动以当前的鼠标坐标为圆心,画出目标半长轴、半短轴和颜色的椭圆形图案。

6:设计具有宽度的画线算法,并处理线条连接处出现缺口的问题。

实验结果如下图所示:
第一步:编写代码查看未处理缺口的图像,以便后续进行对比。【此处以第一条直线起点(0,0)、终点(100,100),第二条直线起点(100,100)、终点(400,300)为例】


放大后可观察到连接处有一定程度的缺口。

 

第二步:通过改进bresenham画线算法,弥补缺口。

方法一:通过硬修改直线端点处的宽度绘画法连接两条直线。【此处以第一条直线起点(0,0)、终点(100,100),第二条直线起点(100,100)、终点(400,300)为例】
输入坐标数据内容:


程序生成图像:


放大后可观察到连接处较平稳:但是缺点明显,比如斜率绝对值过度小于1的直线宽度从视觉上来说不够。

 

方法二:通过改进bresenham算法,将putpixel()函数改进为minepixel()函数,通过方形刷子的方法进行缺口填缺。【此处以第一条直线起点(100,100)、终点(200,100),第二条直线起点(200,100)、终点(400,400)为例】
输入坐标数据内容:

程序生成图像:


放大后可观察到连接处较平稳:

 

 

四 程序说明

Project中程序的调用:

将当前cpp文件的属性——常规——从生成中排除中选择否,其他文件选择是,即可运行当前的cpp文件

4

//

// 程序名称:画圆算法

// 功  能:用四分法画半径为r的一个完整的圆,用鼠标选择圆心位置

// 编译环境:VS2019,EasyX_20220116

// 最后修改:2022-3-10

#include <graphics.h>

#include <conio.h>

#include <iostream>

using namespace std;

void Circle(int myx, int myy, int r, int color) {

    int x, y, delta, delta1, delta2, direction;

    //画第一象限的圆弧

    x = 0;

    y = r;

    delta = 2 * (1 - r);

    while(y >= 0) {

       putpixel(myx + x, myy + y, color);

       if (delta < 0) {

           delta1 = 2 * (delta + y) - 1;

           if (delta1 <= 0) {

              direction = 1;

           }

           else {

              direction = 2;

           }

       }

       else if (delta > 0) {

           delta2 = 2 * (delta - x) - 1;

           if (delta2 <= 0) {

              direction = 2;

           }

           else {

              direction = 3;

           }

       }

       else {

           direction = 2;

       }

       switch (direction) {

       case 1:

           x++;

           delta += 2 * x + 1;

           break;

       case 2:

           x++;

           y--;

           delta += 2 * (x - y + 1);

           break;

       case 3:

           y--;

           delta += (-2 * y + 1);

           break;

       }

    }

    //画第二象限的圆弧

    x = 0;

    y = r;

    delta = 2 * (1 - r);

    while (y >= 0) {

       putpixel(myx + x, myy + y, color);

       if (delta < 0) {

           delta1 = 2 * (delta + y) - 1;

           if (delta1 <= 0) {

              direction = 1;

           }

           else {

              direction = 2;

           }

       }

       else if (delta > 0) {

           delta2 = 2 * (delta - x) - 1;

           if (delta2 <= 0) {

              direction = 2;

           }

           else {

              direction = 3;

           }

       }

       else {

           direction = 2;

       }

       switch (direction) {

       case 1:

           x--;

           delta += 2 * (-x) + 1;

           break;

       case 2:

           x--;

           y--;

           delta += 2 * (-x - y + 1);

           break;

       case 3:

           y--;

           delta += (-2 * y + 1);

           break;

       }

    }

    //画第三象限的圆弧

    x = 0;

    y = -r;

    delta = 2 * (1 - r);

    while (y <= 0) {

       putpixel(myx + x, myy + y, color);

       if (delta < 0) {

           delta1 = 2 * (delta - y) - 1;

           if (delta1 <= 0) {

              direction = 1;

           }

           else {

               direction = 2;

           }

       }

       else if (delta > 0) {

           delta2 = 2 * (delta + x) - 1;

           if (delta2 <= 0) {

              direction = 2;

           }

           else {

              direction = 3;

           }

       }

       else {

           direction = 2;

       }

       switch (direction) {

       case 1:

           x--;

           delta += 2 * (-x) + 1;

           break;

       case 2:

           x--;

           y++;

           delta += 2 * (-x + y + 1);

           break;

       case 3:

           y++;

           delta += (2 * y + 1);

           break;

       }

    }

    //画第四象限的圆弧

    x = 0;

    y = -r;

    delta = 2 * (1 - r);

    while (y <= 0) {

       putpixel(myx + x, myy + y, color);

       if (delta < 0) {

           delta1 = 2 * (delta - y) - 1;

           if (delta1 <= 0) {

              direction = 1;

           }

           else {

              direction = 2;

           }

       }

       else if (delta > 0) {

           delta2 = 2 * (delta - x) - 1;

           if (delta2 <= 0) {

              direction = 2;

           }

           else {

              direction = 3;

           }

       }

       else {

           direction = 2;

       }

       switch (direction) {

       case 1:

           x++;

           delta += 2 * x + 1;

           break;

       case 2:

           x++;

           y++;

           delta += 2 * (x + y + 1);

           break;

       case 3:

           y++;

           delta += (2 * y + 1);

           break;

       }

    }

}

int main() {

    //用户定义圆的相关参数

    int r,colornum;

    cout << "please input the radius of your circle" << endl;

    cin >> r;

    cout << endl;

    cout << "please choose a number to define the color of your circle" << endl;

    cout << "0:WHITE 1:RED 2:YELLOW" << endl;

    cin >> colornum;

    int colorarray[3] = { WHITE,RED,YELLOW };

    //图形界面

    initgraph(640, 480);

    ExMessage m;

    int x0, y0, x1, y1;

    while (true) {

       m = getmessage(EX_MOUSE | EX_KEY);

       switch (m.message) {

       case WM_LBUTTONDOWN:

           x0 = m.x;

           y0 = m.y;

           setfillcolor(GREEN);

           fillrectangle(m.x - 3, m.y - 3, m.x + 3, m.y + 3);

           Circle(m.x, m.y, r, colorarray[colornum]);

       case WM_KEYDOWN:

           if (m.vkcode == VK_ESCAPE)

              return 0;  // 按 ESC 键退出程序

       }

    }

    closegraph();

    return 0;

}

5

//

// 程序名称:画椭圆算法

// 功  能:画一个半长轴为a和半短轴为b的完整椭圆,用鼠标选择圆心位置

// 编译环境:VS2019,EasyX_20220116

// 最后修改:2022-3-10

#include <graphics.h>

#include <conio.h>

#include <iostream>

#include <cmath>

using namespace std;

void Oval(int myx, int myy, int a, int b, int color) {

    double xp, yp, d;

    int x, y;

    //第一象限

    xp = (double)a * a / sqrt(a * a + b * b);

    yp = (double)b * b / sqrt(a * a + b * b);

    //(0,b)=>p

    x = 0;

    y = b;

    d = b * b + (-b + 0.25) * a * a;

    while (x <= xp) {

       putpixel(x + myx, y + myy, color);

       if (d <= 0) {

           d = d + (2 * x + 3) * b * b;

           x++;

       }

       else {

           d = d + (x * 2 + 3) * b * b + (-2 * y + 2) * a * a;

           x++;

           y--;

       }

    }

    //p->(a,0)

    x = a;

    y = 0;

    d = (-a + 0.25) * b * b + a * a;

    while (y < yp) {

       putpixel(myx + x, myy + y, color);

       if (d <= 0) {

           d += (2 * y + 3) * a * a;

           y++;

       }

       else {

           d += (2 * y + 3) * a * a + (-2 * x + 2) * b * b;

           x--;

           y++;

       }

    }

    //第二象限

    xp = (double)-a * a / sqrt(a * a + b * b);

    yp = (double)b * b / sqrt(a * a + b * b);

    //(0,b)=>p

    x = 0;

    y = b;

    d = b * b + (-b + 0.25) * a * a;

    while (x >= xp) {

       putpixel(x + myx, y + myy, color);

       if (d <= 0) {

           d = d + (-2 * x + 3) * b * b;

           x--;

       }

       else {

           d = d + (-x * 2 + 3) * b * b + (-2 * y + 2) * a * a;

           x--;

           y--;

       }

    }

    //p->(a,0)

    x = -a;

    y = 0;

    d = (-a + 0.25) * b * b + a * a;

    while (y < yp) {

       putpixel(myx + x, myy + y, color);

       if (d <= 0) {

           d += (2 * y + 3) * a * a;

           y++;

       }

       else {

           d += (2 * y + 3) * a * a + (2 * x + 2) * b * b;

           x++;

           y++;

       }

    }

    //第三象限

    xp = (double)-a * a / sqrt(a * a + b * b);

    yp = (double)-b * b / sqrt(a * a + b * b);

    //(0,-b)=>p

    x = 0;

    y = -b;

    d = b * b + (-b + 0.25) * a * a;

    while (x >= xp) {

       putpixel(x + myx, y + myy, color);

       if (d <= 0) {

           d = d + (-2 * x + 3) * b * b;

           x--;

       }

       else {

           d = d + (-x * 2 + 3) * b * b + (2 * y + 2) * a * a;

           x--;

           y++;

       }

    }

    //p->(-a,0)

    x = -a;

    y = 0;

    d = (-a + 0.25) * b * b + a * a;

    while (y >= yp) {

       putpixel(myx + x, myy + y, color);

       if (d <= 0) {

           d += (-2 * y + 3) * a * a;

           y--;

       }

       else {

           d += (-2 * y + 3) * a * a + (2 * x + 2) * b * b;

           x++;

           y--;

       }

    }

    //第四象限

    xp = (double)a * a / sqrt(a * a + b * b);

    yp = (double)-b * b / sqrt(a * a + b * b);

    //(0,-b)=>p

    x = 0;

    y = -b;

    d = b * b + (-b + 0.25) * a * a;

    while (x <= xp) {

       putpixel(x + myx, y + myy, color);

       if (d <= 0) {

           d = d + (2 * x + 3) * b * b;

           x++;

       }

       else {

           d = d + (x * 2 + 3) * b * b + (2 * y + 2) * a * a;

           x++;

           y++;

       }

    }

    //p->(a,0)

    x = a;

    y = 0;

    d = (-a + 0.25) * b * b + a * a;

    while (y >= yp) {

       putpixel(myx + x, myy + y, color);

       if (d <= 0) {

           d += (-2 * y + 3) * a * a;

           y--;

       }

       else {

           d += (-2 * y + 3) * a * a + (-2 * x + 2) * b * b;

           x--;

           y--;

       }

    }

}

int main() {

    //用户定义椭圆的相关参数

    int a, b, colornum;

    cout << "please input the long and short parameters of your oval" << endl;

    cin >> a >> b;

    cout << endl;

    cout << "please choose a number to define the color of your oval" << endl;

    cout << "0:WHITE 1:RED 2:YELLOW" << endl;

    cin >> colornum;

    int colorarray[3] = { WHITE,RED,YELLOW };

    //图形界面

    initgraph(640, 480);

    ExMessage m;

    while (true) {

       m = getmessage(EX_MOUSE | EX_KEY);

       switch (m.message) {

       case WM_LBUTTONDOWN:

           setfillcolor(GREEN);

           fillrectangle(m.x - 3, m.y - 3, m.x + 3, m.y + 3);

           Oval(m.x, m.y, a, b, colorarray[colornum]);

       case WM_KEYDOWN:

           if (m.vkcode == VK_ESCAPE)

              return 0;  // 按 ESC 键退出程序

       }

    }

    closegraph();

    return 0;

}

6题 方法1

//

// 程序名称:有宽度线段的连接处处理

// 功  能:设计具有宽度的画线算法,并处理线条连接处出现缺口的问题

// 编译环境:VS2019,EasyX_20220116

// 最后修改:2022-3-10

#include <graphics.h>

#include <conio.h>

#include <iostream>

#include <cmath>

using namespace std;

void bresenham(int x0, int y0, int x1, int y1, int color) {

    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;

    int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;

    int erro = (dx > dy ? dx : -dy) / 2;

    while (putpixel(x0, y0, color), x0 != x1 || y0 != y1) {

       int e2 = erro;

       if (e2 > -dx) {

           erro -= dy;

           x0 += sx;

       }

       if (e2 < dy) {

           erro += dx;

           y0 += sy;

       }

    }

}

int main() {

    //用户定义相关参数

    int x0, y0, x1, y1, width, x2, y2, x3, y3;

    cout << "Please input the first starting point:" << endl;

    cin >> x0 >> y0;

    cout << "Please input the first ending point:" << endl;

    cin >> x1 >> y1;

    cout << "Please input the second starting point:" << endl;

    cin >> x2 >> y2;

    cout << "Please input the second ending point:" << endl;

    cin >> x3 >> y3;

    //cout << "Please input the half width of your lines:" << endl;

    //cin >> width;//输入半宽度

    int mycolor[2] = { RED, YELLOW };

    /*

    double flag1 = (double)(y1 - y0) / (x1 - x0);

    double flag2 = (double)(y3 - y2) / (x3 - x2);

    //flag1>1 or flag1<-1 斜率大于1 采用横向补点

    //flag1<1 and flag1>-1 斜率小于1 采用纵向补点

    //flag2同理可得

    */

    //图形界面

    initgraph(640, 480);

    //线性加粗

    bresenham(x0, y0, x1, y1, mycolor[0]);

    //宽度确定为10像素

    for (int i = 0; i < 5; i++) {

       bresenham(x0 + i, y0, x1 + i, y1, mycolor[0]);

       bresenham(x0 - i, y0, x1 - i, y1, mycolor[0]);

    }

    for (int i = 0; i < 5; i++) {

       bresenham(x2 + i, y2, x3 + i, y3, mycolor[1]);

       bresenham(x2 - i, y2, x3 - i, y3, mycolor[1]);

    }

    //补充缺口

    _getch();

    closegraph();

    return 0;

}

6题 方法2

//

// 程序名称:有宽度线段的连接处处理

// 功  能:设计具有宽度的画线算法,并处理线条连接处出现缺口的问题

// 编译环境:VS2019,EasyX_20220116

// 最后修改:2022-3-10

#include <graphics.h>

#include <conio.h>

#include <iostream>

#include <cmath>

using namespace std;

void mineputpixel(int x, int y, int color) {

    putpixel(x, y, color);

    putpixel(x-1, y, color);

    putpixel(x+1, y, color);

    putpixel(x-1, y+1, color);

    putpixel(x, y+1, color);

    putpixel(x+1, y+1, color);

    putpixel(x, y-1, color);

    putpixel(x+1, y-1, color);

    putpixel(x-1, y-1, color);

}

void bresenham(int x0, int y0, int x1, int y1, int color) {

    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;

    int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;

    int erro = (dx > dy ? dx : -dy) / 2;

    while (mineputpixel(x0, y0, color), x0 != x1 || y0 != y1) {

       int e2 = erro;

       if (e2 > -dx) {

           erro -= dy;

           x0 += sx;

       }

       if (e2 < dy) {

           erro += dx;

           y0 += sy;

       }

    }

}

int main() {

    //用户定义相关参数

    int x0, y0, x1, y1, x2, y2, x3, y3;

    cout << "Please input the first starting point:" << endl;

    cin >> x0 >> y0;

    cout << "Please input the first ending point:" << endl;

    cin >> x1 >> y1;

    cout << "Please input the second starting point:" << endl;

    cin >> x2 >> y2;

    cout << "Please input the second ending point:" << endl;

    cin >> x3 >> y3;

    int mycolor[2] = { RED, YELLOW };

    //图形界面

    initgraph(640, 480);

    bresenham(x0, y0, x1, y1, mycolor[0]);

    bresenham(x2, y2, x3, y3, mycolor[1]);

    _getch();

    closegraph();

    return 0;

}