实验二 实现中点分割直线段裁剪算法

一、目的

1. 了解直线裁剪的基本原理和常用方法。
2. 掌握中点分割直线段裁剪算法的基本原理和步骤。
3. 使用C++、OpenGL编程实现如下内容:输入直线段的起始点和终止点坐标位置、用四边形模拟显示器的边界,利用中点分割直线段裁剪算法计算直线在模拟显示器内部的裁剪结果。

二、实验设备

编程语言:C++、OpenGL
实验环境:已安装相关编程环境的计算机1台。

三、算法原理

中点分割直线段裁剪算法对Cohen-Sutherland直线裁剪算法的第3种情况做了改进,原理是简单地把起点为P0,终点为P1的直线段等分为两段直线P0P和PP1(P为直线段中点),对每一段直线重复“简取”和“简弃”的处理,对于不能处理的直线段再继续等分下去线,直至每一段直线完全能够被“简取”或“简弃”,也就是说直至每段直线完全位于窗口之内或完全位于窗口之外,就完成了直线段的裁剪工作。
算法步骤
(1)以P1出发,检测点P2是否在窗口内,若是,即是所求点;若不转(2)
(2)检测点P1P2是否在同一外侧,若是,则不在窗口内;返回;不转(3)
(3)求出该直线段的中点P3。检测P3与P1P2的关系:
若中点P3在窗口内,用P3代替P1;重复步骤(3);
若中点P3不在窗口内,判断P3与P2的关系,
若P3、P2在同侧外面,则用P3代替P2,即丢掉P2P3段;
若不在同侧外面,则用P3代替P1,重复步骤(3)。直到满足误差条件,则该中点就是该线段落距离P1最远的可见点。
(4)重复步骤(2)-(3),求出另一个端点P2的最远可见点坐标。

四、算法实现及结果分析

#include <GL/glut.h>
#include <cstdio>
#include<iostream>
using namespace std;
#define LEFT 1//左边界
#define RIGHT 2//右边界
#define BOTTOM 4//下边界
#define TOP 8//上边界

/*int x1 = 150, y1 = 50, x2 = 50, y2 = 250, XL = 100, XR = 300, YB = 100, YT = 200;  //(x1,y1)、(x2,y2)为直线段的端点,XL为左边界,XR为右边界,YB为下边界,YT为上边界
int x1_init = 150, y1_init = 50, x2_init = 50, y2_init = 250;  //将直线段端点备份,以便画出裁剪前的直线段*/
double a1, b1, a2, b2, A1, B1, A2, B2, XL = 100, XR = 300, YB = 100, YT = 200;
double x1_init, y1_init, x2_init, y2_init;

int encode(double x, double y)//按位或操作找到该点的空间区域
{
    int c = 0;
    if (x < XL) c |= LEFT;
    if (x > XR) c |= RIGHT;
    if (y < YB) c |= BOTTOM;
    if (y > YT) c |= TOP;
    return c;
}


int border(double mx,double my) {
    int c = 0;
    if (abs(mx - XL) < 1) {
        c |= LEFT;
    }
    if (abs(mx - XR) < 1) {
        c |= RIGHT;
    }
    if (abs(my - YB) < 1) {
        c |= BOTTOM;
    }
    if (abs(my - YT) < 1) {
        c |= TOP;
    }
    return c;
}
bool pm(double mx, double my) {
    if (abs(mx - XL) < 1 || abs(mx - XR) < 1 || abs(my - YB) < 1 || abs(my - YT) < 1) {
        return false;
    }
    else {
        return true;
    }
}
bool Mid_LineClip1() {
    double mx1, my1;
    int code1, code2, code;
    A2 = a2, B2 = b2;//确保(a2,b2)不被更改
    code1 = encode(a1, b1);//得到该点的空间区域
    code2 = encode(A2, B2);
    mx1 = (a1 + A2) / 2;
    my1 = (b1 + B2) / 2;
    code = encode(mx1, my1);
    
    if (code1 == 0){
        A1 = a1, B1 = b1;
        return true;
    }
    else if((code1&code2)!=0){//在窗口外同一侧
        A1 = a1, B1 = b1;
        return false;
    }
    else if ((code1 & border(mx1, my1)) != 0) {//中点在边界上,若靠近点1一侧不在经过窗口
        a1 = mx1;
        b1 = my1;
    }
    else{
        do{
            mx1 = (a1 + A2) / 2;
            my1 = (b1 + B2) / 2;
            code1 = encode(a1, b1);//得到该点的空间区域
            code2 = encode(A2, B2);
            code = encode(mx1, my1);

            if (code == 0) {//中点在窗口内就更新点2(备份)的位置
                A2 = mx1;
                B2 = my1;
                code2 = encode(A2, B2);
            }
            else {
                if ((code & code1) != 0) {//中点在窗口外和点1同一侧就更新点1的位置
                    a1 = mx1;
                    b1 = my1;
                    code1 = encode(a1, b1);
                }
                else {//中点在窗口外和点2在同一侧就更新点2的位置
                    A2 = mx1;
                    B2 = my1;
                    code2 = encode(A2, B2);
                }
            }
        } while (pm(a1, b1));
    }
    /*if (abs(a1 - XL) < 5) {//调整误差
        a1 = XL;
    }
    else if(abs(a1-XR)<5){
        a1 = XR;
    }
    if (abs(b1 - YB) < 5) {
        b1 = YB;
    }
    else if(abs(b1 - YT) < 5){
        b1 = YT;
    }*/
    A1 = a1, B1 = b1;//保留更新的点1
    //cout << "a1 " << a1 << "b1 " << b1 << endl;
    //cout << "A2 " << A2 << "B2 " << B2 << endl;
    return true;
}

bool Mid_LineClip2() {
    double mx2, my2;
    int code1, code2, code;
    code1 = encode(A1, B1);//得到该点的空间区域
    code2 = encode(a2, b2);
    mx2 = (A1 + a2) / 2;
    my2 = (B1 + b2) / 2;
    code = encode(mx2, my2);
    if (code2 == 0) {
        return true;
    }
    else if ((code2 & border(mx2, my2)) != 0) {//中点在边界上,若靠近点2一侧不在经过窗口
        a2 = mx2;
        b2 = my2;
    }
    else {
        do {
            mx2 = (A1 + a2) / 2;
            my2 = (B1 + b2) / 2;
            code1 = encode(A1, B1);//得到该点的空间区域
            code2 = encode(a2, b2);
            code = encode(mx2, my2);
            if (code == 0) {//中点在窗口内就更新点1的位置
                A1 = mx2;
                B1 = my2;
                code1 = encode(A1, B1);
            }
            else {//中点在窗口外和点2同一侧就更新点2的位置
                a2 = mx2;
                b2 = my2;
                code2 = encode(a2, b2);
            }
        } while (pm(a2, b2));
    }
    /*if (abs(a2 - XL) < 5) {//调整误差
        a2 = XL;
    }
    else if (abs(a2 - XR) < 5) {
        a2 = XR;
    }
    if (abs(b1 - YB) < 5) {
        b2 = YB;
    }
    else if (abs(a2 - XR) < 5) {
        b2 = YT;
    }*/
    //cout << "A1 " << A1 << "B1 " << B1 << endl;
    //cout << "a2 " << a2 << "b2 " << b2 << endl;
    return true;
}

void init()  //初始化函数
{
    glClearColor(0.0, 0.0, 0.0, 0.0);  //设置背景颜色
    glMatrixMode(GL_PROJECTION);       // 设置投影参数
    gluOrtho2D(0.0, 600.0, 0.0, 400.0); // 设置场景的大小
    //CS_LineClip();  //执行一次裁剪算法
    Mid_LineClip1();
    Mid_LineClip2();
}

void mydisplay()  //显示函数
{
    //绘制方形边界
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 0.0, 0.0);
    glPointSize(2);
    glBegin(GL_LINE_LOOP);
    glVertex2i(XL, YT);
    glVertex2i(XL, YB);
    glVertex2i(XR, YB);
    glVertex2i(XR, YT);
    glEnd();
    glFlush();
    //绘制未裁剪前的线段
    glBegin(GL_LINES);
    glVertex2i(x1_init, y1_init);
    glVertex2i(x2_init, y2_init);
    glEnd();
    glFlush();
    //绘制裁剪后的线段
    glColor3f(1.0, 1.0, 0.0);
    glBegin(GL_LINES);
    glVertex2i(a1, b1);
    glVertex2i(a2, b2);
    glEnd();
    glFlush();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(400, 400);
    glutCreateWindow("Cohen-Sutherland裁剪算法");
    /*printf("请输入直线端点");
    scanf_s("%d%d%d%d", &a1, &b1, &a2, &b2);*/
    cout<<"请输入直线端点"<<endl;
    cin >> a1 >> b1 >> a2 >> b2;
    x1_init = a1, y1_init = b1, x2_init = a2, y2_init = b2;
    init();
    glutDisplayFunc(&mydisplay);
    glutMainLoop();
    return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在本次实验中,我第一个遇到的问题就是变量 y1 和 C++ 标准库中的y1()函数冲突。我们可以通过改变量名或者使用c语言的标准去写,最终解决了问题。第二个遇到的问题是精度的问题,如果我们用int去对中点与边界的距离是否小于像素点时,误差会比较大。我们可以通过double去细化精度,或者通过判断该点与边界的位置关系将其位移到边界上,这样就提高了画线的精度。第三个遇到的问题就是情况分类,初次完成的代码只能成功画出某些满足条件的直线,但是当遇到直线中点位于边界的情况,就会产生错误。所以,我添加了一个border()函数用于判断靠近哪个边界。到目前为止,参与测试的直线都能够成功画出。

  • 1
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Cohen-Sutherland直线裁剪算法是一种用于裁剪直线算法。它通过对直线的端点进行编码,并利用位运算来决定是否需要进行裁剪算法的步骤如下: 1. 将裁剪窗口分为九个区域,对应于进制编码的九个位: - 0000: 线完全在裁剪窗口内部 - 0001: 线与窗口左边界相交 - 0010: 线与窗口右边界相交 - 0100: 线与窗口下边界相交 - 1000: 线与窗口上边界相交 - 0101: 线与窗口左下角相交 - 1001: 线与窗口左上角相交 - 0110: 线与窗口右下角相交 - 1010: 线与窗口右上角相交 2. 对直线的两个端点进行编码,并根据编码判断线是否需要进行裁剪。 - 如果两个端点的编码都为0000,表示线完全在裁剪窗口内部,无需裁剪。 - 如果两个端点的编码的逻辑与运算结果不为0000,表示线与窗口有交点,需要进行裁剪。 - 如果两个端点的编码的逻辑与运算结果为0000,表示线与窗口无交点,且整条线都在裁剪窗口外部,无需裁剪。 3. 如果线需要进行裁剪,则根据需要裁剪的边界进行处理: - 如果线与左边界相交,则根据线方向求出相交点的坐标,并更新线的起点。 - 如果线与右边界相交,则根据线方向求出相交点的坐标,并更新线的终点。 - 如果线与下边界相交,则根据线方向求出相交点的坐标,并更新线的起点。 - 如果线与上边界相交,则根据线方向求出相交点的坐标,并更新线的终点。 4. 重复步骤2和步骤3,直到线不需要裁剪或者已经被完全裁剪中点分割算法是另一种裁剪直线算法。它通过递归地将线分割为左半边和右半边,并对每一半进行裁剪,直到无法再分割或者线完全在裁剪窗口内部。 这两种算法都可以用来实现直线裁剪,具体选择哪种算法取决于具体的应用场景和需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值