计算机图形学上机报告(五)

1.了解Cohen-Sutherland算法、中点分割算法、重新构造算法三种直线裁剪算法的原理;

2.利用C++语言编程实现Cohen-Sutherland直线裁剪算法;

Visual Studio 2022版

Windows 10/11

假设窗口是标准矩形,即边与坐标轴平行的矩形,由上(y=yt)、下(y=yb)、左(x=xl)、右(x=xr)四条边描述。

对于点与窗口的位置关系,判断如下:

对于任意一点P(x,y),若同时满足下列条件:

xl<x<xr

yb<y<yt

则点P在矩形窗口内;否则,点P在矩形窗口外。

对每条直线段P1(x1,y1)、P2(x2,y2)分三步处理:

(1)首先快速判断点P1P2是否完全在裁减窗口内,直线在窗口内,两个端点应该同时满足在窗口内的条件。若满足以上条件,直线段完全可见,“简取”之;若不满足以上条件,则进行下一步。

(2)接着快速判断P1P2是否完全在裁剪窗口之外。直线在窗口外,只需要满足以下四个条件之一即可:

x1<xl&x2<xl

x1>xr&x2>xr

y1>yt&y2>yt

y1<yb&y2<yb

若判断出直线在窗口外,则“简弃”之;若也不满足这个条件,则直线可能与窗口相交,进行下一步。

(3)求线段与窗口边延长线的交点,这个交点将线段分为两段,其中一段显然不可见,将其丢弃。对余下的另一段重新进行第一步,第二步判断,直至结束。

为了对这两种情况进行快速判别,Cohen-Sutherland采用编码的方式,由窗口四条边所在直线把二维平面分成9个区域,每个区域赋予一个4位的二进制编码D4D3D2D1。编码规则如下:若x1<xl则D1=1,否则D1=0;若x1>xr,则D2=1,否则D2=0;若y1<yb,则D3=1,否则D3=0;若y2>yt,则D4=1,否则D4=0。即D4D3D2D1分别对应上下右左区域,左上区域为1001,左中区域为0001,左下区域为0101,右上区域为1010,右中区域为0010,右下区域为0110,中上区域为1000,中间区域为0000,中下区域为0100。这样,当线段的两个端点的编码的按位逻辑与非零时,线段不可能以任何一种形式穿过窗口,显然是不可见的;当线段的两个端点的编码的逻辑或为零时,线段一定在窗口内部为完全可见的。若不满足以上两种情况,则进行下一步操作。确保P1在窗口外部,若P2在窗口内,则交换P1P2的坐标值和编码,调换两点位置。接着按左、右、上、下的顺序逐一判断是否有与窗口边界的交点位置,并求出相应位置直线段与窗口边界的交点S,并用该交点的坐标值替换P1的坐标值,也即在交点S处把线段一分为二,并去掉P1S这一段。此时P2S即为直线段P1-P2,画出这条直线段即可。对于交点的坐标,可以利用解析几何的方法进行推导,P1(x1,y1)、P2(x2,y2)组成的直线段与上下边界的交点为:

y=y1+y2-y1×xl/r-x1x2-x1

P1(x1,y1)、P2(x2,y2)组成的直线段与上下边界的交点为:

x=x1+x2-x1×yt/b-y1y2-y1

//对区域进行编码

#define LEFT 1//0001

#define RIGHT 2//0010

#define TOP 8//1000

#define BOTTOM 4//0100

//头文件

#include<iostream>

#include<graphics.h> //包含画图函数

#include <conio.h>//输入输出图形

#include<math.h> //数学函数库

using namespace std;

int xmin, xmax, ymin, ymax;//窗口范围

int gencode(float x, float y) {

    int code = 0;//初始编码落在窗口范围内,编码为0000

    if (x < xmin)

         code = code | LEFT;//|为按位或运算,对左侧区域进行编码

    if (x > xmax)

         code = code | RIGHT;//对右侧区域进行编码

    if (y < ymin)

         code = code | TOP;//对上侧区域进行编码

    if (y > ymax)

         code = code | BOTTOM;//对下侧区域进行编码

    return code;

}//这个函数可以根据坐标值对一个屏幕范围内的点进行编码

void draw_rectangle() {

    int x, y;

    y = ymin;

    for (x = xmin; x <= xmax; x++)

         putpixel(x, y, WHITE);

    y = ymax;

    for (x = xmin; x <= xmax; x++)

         putpixel(x, y, WHITE);

    x = xmin;

    for (y = ymin; y <= ymax; y++)

         putpixel(x, y, WHITE);

    x = xmax;

    for (y = ymin; y <= ymax; y++)

         putpixel(x, y, WHITE);

}//画出窗口

void CohenSutherland(float x1, float y1, float x2, float y2) {

    bool done = false;//初始化done值为0

    int code1, code2;//两个端点的编码值

    do {

         code1 = gencode(x1, y1);//为第一个点编码

         code2 = gencode(x2, y2);//为第二个点编码

         if (code1 == 0 && code2 == 0) {

             line(x1, y1, x2, y2);

             done = true;

         }//两个点都在窗口内,简取,直接画线即可

         else if ((code1 & code2) != 0) {

             done = false;

         }//两个点都在窗口外,简弃,不用进行任何操作

         else {

             int codeout;//窗口外部点的编码

             float x, y;

             if (code1 != 0)

                  codeout = code1;

             else

                  codeout = code2;//取出位于窗口外部点的编码

             if (codeout & LEFT) {

                  y = y1 + (y2 - y1) * (xmin - x1) / (x2 - x1);

                  x = xmin;//求左交点的坐标值

             }

             else if (codeout & RIGHT) {

                  y = y1 + (y2 - y1) * (xmax - x1) / (x2 - x1);

                  x = xmax;//求右交点的坐标值

             }

             else if (codeout & TOP) {

                  x = x1 + (x2 - x1) * (ymin - y1) / (y2 - y1);

                  y = ymin;//求上交点的坐标值

             }

             else if (codeout & BOTTOM) {

                  x = x1 + (x2 - x1) * (ymax - y1) / (y2 - y1);

                  y = ymax;//求下交点的坐标值

             }

             if (codeout == code1) {

                  x1 = x;

                  y1 = y;

                  code1 = gencode(x1, y1);

             }

             else {

                  x2 = x;

                  y2 = y;

                  code1 = gencode(x2, y2);

             }//若窗口外的点为P1,则将P1改到窗口边界位置,并更新P1的编码,若窗口外的点为P2,则将P2改到窗口边界位置,交换P1、P2坐标

         }

    } while (!done);//当两个端点都在窗口内部才停止操作

    line(x1, y1, x2, y2);//画出裁剪之后的直线

}//这个函数可以根据两个点坐标和窗口位置对直线进行裁剪,并绘制出裁剪后的直线

//主函数

int main() {

    initgraph(600, 600);//初始化图窗

    xmin = 100;

    ymin = 100;

    xmax = 500;

    ymax = 500;//建立窗口四个边界

    draw_rectangle();//画出窗口

    float x1, y1, x2, y2;

    x1 = 400;

    y1 = 0;

    x2 = 0;

    y2 = 400;//直线两端点坐标值

    CohenSutherland(x1, y1, x2, y2);//进行裁剪并绘制出裁剪后的直线

    _getch();

    closegraph();

    return 0;

}

1.Cohen-Sutherland直线裁剪算法

(1)直线两端点均位于窗口外

(2)直线一个端点位于窗口内部、一个端点位于窗口外部

(3)两个端点都位于窗口外部

(4)两个端点都位于窗口内部

本次实验主要利用C++语言编程实现了Cohen-Sutherland直线裁剪算法。在了解算法原理后,可以直接根据算法的步骤,直接按照步骤编写实现每一步的函数即可,程序中多为简单的选择结构和循环结构,很好进行编写,此处便不赘述程序编写方法。

对于Cohen-Sutherland直线裁剪算法,其编码的思想十分巧妙,通过对区域进行编码的方式和进行逻辑运算,直接判断出直线是否完全在窗口内部或完全在窗口外部,这样的算法设计也极大程度地减小了编程的工作量,在程序中只需要一个判断语句就可以得到这个位置关系,而不需要再编写复杂的函数来进行实现,这也在一定程度上提高了算法效率。而其区域编码的方式也很巧妙,刚好能够满足当线段的两个端点的编码的按位逻辑与非零时,线段不以任何一种形式穿过窗口,即使两个端点都在窗口外部,也不可能穿过窗口;当线段的两个端点的编码的逻辑或为零时,线段两端点都在窗口内部。

在算法效率方面,Cohen-Sutherland直线裁剪算法由于采用编码计算的方式,没有冗余循环,其主要功能函数中只有一层循环,其他编码函数中不存在循环,所以算法时间复杂度为O(n),十分高效。相比于其他两种直线裁剪算法,即中点分割算法和重新构造算法而言,在进行直线与图窗的位置关系判断方面,三种算法都采用编码的方式,对于完全处于图窗外部的直线进行“简弃”,对于完全处于图窗内部的直线进行“简取”;三种算法主要的区别之处在于对于直线与多边形交点的判断,中点分割算法采用不断分割直线并保留裁剪图形内部直线段的方式,最后将一系列标记保留的直线段相连,得到裁剪后的直线,整个过程并不需要求取交点,但是需要使用到栈的数据结构,编程思路相对复杂,且循环次数多,算法效率较低,且最终求取的裁剪后直线与裁剪窗口的交点不一定准确,对于重新构造算法而言,其求取直线与裁剪窗口交点的方法为利用二分法不断对于交点位置进行逼近,最终得到一个处于极小范围内的交点坐标,但是这个交点坐标也不是绝对准确的,且算法循环次数较多,算法效率较低。所以相对而言,Cohen-Sutherland直线裁剪算法是思路最为直接,编程最为简便,且效率最高,交点判断准确性最强的直线裁剪算法。

本次通过不同情况下的裁剪验证了程序的准确性,对于Cohen-Sutherland直线裁剪算法验证了直线完全处于窗口外和完全处于窗口内以及与窗口相交的几种不同情况,都成功裁剪,说明程序的准确性和普适性

Cohen-Sutherland直线裁剪算法也有很多应用,例如在进行一些分析时,如在GIS中裁剪位于一定区域范围的河流、道路等地物,或是裁剪遥感影响边缘的线状物体,都需要用到直线裁剪算法,可能其使用的不是这种直线裁剪算法,已经有了完好的封装,也无需我们对其进行编程。但是编码是一种很新颖的算法设计思路,掌握这种算法对于今后的算法设计思路和编程能力都是一种提升。

注意:本上机报告适用于中国地质大学(北京)测绘工程专业计算机图形学课程,其他学校也可以参考使用。这些代码只是起到启发作用,不能完全照搬(可能会有抄作业的嫌疑哦(手动狗头)),具体细节还需要自己斟酌修改。

如有问题欢迎在评论区留言,让我们共同交流,一起进步!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值