【计算机图形学】裁剪算法(Cohen-Sutherland算法 & 中值分割算法 & Liang-Barsky算法)

一 实验目的

  1. 编写直线段、多边形裁剪算法
  2. 熟悉Cohen-Sutherland算法、中值分割算法和Liang-Barsky算法的裁剪

二 实验算法理论分析
Cohen-Sutherland算法:

 

 

中值分割算法:

与CS算法一样,首先对直线段端点进行编码,并把线段与窗口的关系一样分为3种情况:全在、完全不在、线段和窗口有交点,并对前两种情况进行一样的处理。对于第3种情况,则用中点分割的方法简单地把线段等分为两段,对两段重复上述测试处理,直至每条线段完全在窗口内和完全在窗口外。

可行性分析:计算机屏幕是有限的,比如1024×768个像素,x方向是2的10次方。所以这样一直二分下去的话,最多分10次。分到第十次的时候,就到像素级了,就不用再分了。所以最多循环10次。

 Liang-Barsky算法:

三 实验内容

1:用Cohen-Sutherland算法实现直线段裁剪

实验结果如下图所示:
第一步:依次输入A点的横坐标和纵坐标、B点的横坐标和纵坐标(此处以【0,0】为A点坐标,【400,400】为B点坐标为例)。

 

 第二步:用户勾选需要裁剪的红色框,并将存在于矩形框内的AB线段用白色部分展示出来(此处以用户第一次点击为矩形框的左下位置,第二次点击为矩形框的右上位置为例)。


裁剪前:

 

裁剪后:

 

2:用中值分割算法实现直线段裁剪

实验结果如下图所示:
第一步:依次输入A点的横坐标和纵坐标、B点的横坐标和纵坐标(此处以【0,0】为A点坐标,【400,400】为B点坐标为例)。

 

 第二步:用户勾选需要裁剪的红色框,并将存在于矩形框内的AB线段用白色部分展示出来(此处以用户第一次点击为矩形框的左下位置,第二次点击为矩形框的右上位置为例)。

裁剪前:

裁剪后:

3:用Liang-Barsky算法实现直线段裁剪

实验结果如下图所示:
第一步:依次输入A点的横坐标和纵坐标、B点的横坐标和纵坐标(此处以【0,0】为A点坐标,【400,250】为B点坐标为例)。

 

 第二步:用户勾选需要裁剪的红色框,并将存在于矩形框内的AB线段用白色部分展示出来(此处以用户第一次点击为矩形框的左下位置,第二次点击为矩形框的右上位置为例)。

裁剪前:

 裁剪后:

四 程序说明

Project中程序的调用:

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

1

//

// 程序名称:CS裁剪

// 功    能:用Cohen-Sutherland算法实现直线段裁剪

// 编译环境:VS2019,EasyX_20220116

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

#include <graphics.h>

#include <conio.h>

#include <iostream>

using namespace std;

#define LEFT 1

#define RIGHT 2

#define BOTTOM 4

#define TOP 8

int XL, XR, YB, YT;

int encode(float x, float y, int* code) {

    int c = 0;

    if (x < XL) {

         c = c | LEFT;

    }

    else if (x > XR) {

         c = c | RIGHT;

    }

    if (y < YB) {

         c = c | BOTTOM;;

    }

    else if (y > YT) {

         c = c | TOP;

    }

    *code = c;

    return 0;

}

int CSLineClip(float x1, float y1, float x2, float y2) {

    int code1, code2, code;

    float x, y;

    encode(x1, y1, &code1);

    encode(x2, y2, &code2);

    while (code1 != 0 || code2 != 0) {

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

             return 0;

         }

         code = code1;

         if (code1 == 0) {

             code = code2;

         }

         if ((LEFT & code) != 0) {

             x = XL;

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

         }

         else if ((RIGHT & code) != 0) {

             x = XR;

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

         }

         else if ((BOTTOM & code) != 0) {

             y = YB;

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

         }

         else if ((TOP & code) != 0) {

             y = YT;

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

         }

         //

         if (code == code1) {

             x1 = x;

             y1 = y;

             encode(x, y, &code1);

         }

         else {

             x2 = x;

             y2 = y;

             encode(x, y, &code2);

         }

    }

    setlinecolor(WHITE);

    line(x1, y1, x2, y2);

    return 0;

}

int main() {

    //用户定义a、b坐标

    float xa, ya, xb, yb;

    cout << "please input the coordinate of A point:" << endl;

    cin >> xa >> ya;

    cout << "please input the coordinate of B point:" << endl;

    cin >> xb >> yb;

    int x0, y0, x1, y1;

    //0->left bottom; 1->right top;

    //图形界面

    initgraph(640, 480);

    ExMessage m;

    //勾勒AB线段:绿色

    setlinecolor(GREEN);

    line(xa, ya, xb, yb);

    while (true) {

         m = getmessage(EX_MOUSE | EX_KEY);

         switch (m.message) {

         case WM_LBUTTONDOWN:

             x0 = m.x;

             y0 = m.y;

             setlinecolor(WHITE);

             setfillcolor(GREEN);

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

         case WM_RBUTTONDOWN:

             x1 = m.x;

             y1 = m.y;

             setlinecolor(WHITE);

             setfillcolor(GREEN);

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

             //判断用户是左上~右下 / 左下~右上 / 右上~左下 / 右下~左上

             if ((x0 < x1) && (y0 > y1)) {

                  XL = x0;

                  XR = x1;

                  YB = y1;

                  YT = y0;

             }

             else if ((x0 < x1) && (y0 < y1)) {

                  XL = x0;

                  XR = x1;

                  YB = y0;

                  YT = y1;

             }

             else if ((x0 > x1) && (y0 > y1)) {

                  XL = x1;

                  XR = x0;

                  YB = y1;

                  YT = y0;

             }

             else if ((x0 > x1) && (y0 < y1)) {

                  XL = x1;

                  XR = x0;

                  YB = y0;

                  YT = y1;

             }

             //勾勒裁剪框

             setlinecolor(RED);

             line(XL, YT, XR, YT);

             line(XL, YB, XR, YB);

             line(XL, YT, XL, YB);

             line(XR, YT, XR, YB);

             //裁剪部分为白色

             CSLineClip(xa, ya, xb, yb);

         case WM_KEYDOWN:

             if (m.vkcode == VK_ESCAPE)

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

         }

    }

    closegraph();

    return 0;

}

2

//

// 程序名称:中值裁剪

// 功    能:用中值分割算法实现直线段裁剪

// 编译环境:VS2019,EasyX_20220116

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

// Special Thanks To Gong WH

#include <graphics.h>

#include <conio.h>

#include <iostream>

using namespace std;

#define LEFT 1

#define RIGHT 2

#define BOTTOM 4

#define TOP 8

int XL, XR, YB, YT;

//区域编码

int encode(float x, float y, int* code) {

    int c = 0;

    if (x < XL) {

         c = c | LEFT;

    }

    else if (x > XR) {

         c = c | RIGHT;

    }

    if (y < YB) {

         c = c | BOTTOM;;

    }

    else if (y > YT) {

         c = c | TOP;

    }

    *code = c;

    return 0;

}

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

    int code1, code2;

    encode(x1, y1, &code1);

    encode(x2, y2, &code2);

   

    //无法继续二分

    if (abs(x1 - x2) + abs(y1 - y2) <= 2) {

         return;

    }

   

    //线段完全不可见

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

         return;

    }

    //线段完全可见

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

         setlinecolor(WHITE);

         line(x1, y1, x2, y2);

         return;

    }

    //线段部分可见=>递归吧。。

    int midx = (x1 + x2) / 2, midy = (y1 + y2) / 2;

   

    MidClip(midx, midy, x2, y2);

    MidClip(x1, y1, midx, midy);

    /*

    //这个优化,不大行

    int midcode;

    encode(midx, midy, &midcode);

    //中点可见,继续二分

    if (midcode == 0) {

         MidClip(midx, midy, x2, y2);

         MidClip(x1, y1, midx, midy);

    }

    //中点不可见,判断中点和哪个点在同一区域

    else {

         int cnt1[4] = { 0 }, cnt2[4] = { 0 }, cntmid[4] = { 0 };

         //handle midcode

         do {

             if (midcode >= 8) {

                  cntmid[0] = 1;

                  midcode -= 8;

             }

             else if (midcode >= 4) {

                  cntmid[1] = 1;

                  midcode -= 4;

             }

             else if (midcode >= 2) {

                  cntmid[2] = 1;

                  midcode -= 2;

             }

             else if (midcode >= 1) {

                  cntmid[3] = 1;

                  midcode -= 1;

             }

             else {

                  continue;

             }

         } while (midcode != 0);

         //handle code1

         do {

             if (code1 >= 8) {

                  cnt1[0] = 1;

                  code1 -= 8;

             }

             else if (code1 >= 4) {

                  cnt1[1] = 1;

                  code1 -= 4;

             }

             else if (code1 >= 2) {

                  cnt1[2] = 1;

                  code1 -= 2;

             }

             else if (code1 >= 1) {

                  cnt1[3] = 1;

                  code1 -= 1;

             }

             else {

                  continue;

             }

         } while (code1 != 0);

         //handle code2

         do {

             if (code2 >= 8) {

                  cnt2[0] = 1;

                  code2 -= 8;

             }

             else if (code2 >= 4) {

                  cnt2[1] = 1;

                  code2 -= 4;

             }

             else if (code2 >= 2) {

                  cnt2[2] = 1;

                  code2 -= 2;

             }

             else if (code2 >= 1) {

                  cnt2[3] = 1;

                  code2 -= 1;

             }

             else {

                  continue;

             }

         } while (code2 != 0);

         int count1 = 0, count2 = 0;

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

             if (cnt1[i] == cntmid[i]) {

                  count1++;

             }

             if (cnt2[i] == cntmid[i]) {

                  count2++;

             }

         }

         if (count1 > count2) {

             //point 1 area is closer to midpoint area

             MidClip(x1, y1, midx, midy);

         }

         else {

             //point 2 area is closer to midpoint area

             MidClip(midx, midy, x2, y2);

         }

    }

    */

}

int main() {

    //用户定义a、b坐标

    int xa, ya, xb, yb;

    cout << "please input the coordinate of A point:" << endl;

    cin >> xa >> ya;

    cout << "please input the coordinate of B point:" << endl;

    cin >> xb >> yb;

    int x0, y0, x1, y1;

    //0->left bottom; 1->right top;

    //图形界面

    initgraph(640, 480);

    ExMessage m;

    //勾勒AB线段:绿色

    setlinecolor(GREEN);

    line(xa, ya, xb, yb);

    while (true) {

         m = getmessage(EX_MOUSE | EX_KEY);

         switch (m.message) {

         case WM_LBUTTONDOWN:

             x0 = m.x;

             y0 = m.y;

             setlinecolor(WHITE);

             setfillcolor(GREEN);

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

         case WM_RBUTTONDOWN:

             x1 = m.x;

             y1 = m.y;

             setlinecolor(WHITE);

             setfillcolor(GREEN);

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

             //判断用户是左上~右下 / 左下~右上 / 右上~左下 / 右下~左上

             if ((x0 < x1) && (y0 > y1)) {

                  XL = x0;

                  XR = x1;

                  YB = y1;

                  YT = y0;

             }

             else if ((x0 < x1) && (y0 < y1)) {

                  XL = x0;

                  XR = x1;

                  YB = y0;

                  YT = y1;

             }

             else if ((x0 > x1) && (y0 > y1)) {

                  XL = x1;

                  XR = x0;

                  YB = y1;

                  YT = y0;

             }

             else if ((x0 > x1) && (y0 < y1)) {

                  XL = x1;

                  XR = x0;

                  YB = y0;

                  YT = y1;

             }

             //勾勒裁剪框

             setlinecolor(RED);

             line(XL, YT, XR, YT);

             line(XL, YB, XR, YB);

             line(XL, YT, XL, YB);

             line(XR, YT, XR, YB);

             //裁剪部分为白色

             MidClip(xa, ya, xb, yb);

         case WM_KEYDOWN:

             if (m.vkcode == VK_ESCAPE)

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

         }

    }

    closegraph();

    return 0;

}

3

//

// 程序名称:LB裁剪

// 功    能:用Liang-Barsky算法实现直线段裁剪

// 编译环境:VS2019,EasyX_20220116

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

#include <graphics.h>

#include <conio.h>

#include <iostream>

using namespace std;

float XL, XR, YT, YB;

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

    setlinecolor(WHITE);

    line(x1, y1, x2, y2);

}

void LiangBarsky(float x1, float y1, float x2, float y2, float XL, float XR, float YT, float YB) {

    float ansx1, ansx2, ansy1, ansy2;

    //平行于y轴

    if (x1 - x2 == 0) {

        if (x1<XL || x1>XR) {

            return;

        }

        else {

            int ymin = max(YB, min(y1, y2));

            int ymax = min(YT, max(y1, y2));

            if (ymin <= ymax) {

                ansx1 = ansx2 = x1;

                ansy1 = ymin;

                ansy2 = ymax;

            }

            else {

                return;

            }

        }

    }

    //平行于x轴

    else if (y1 - y2 == 0) {

        if (y1<YB || y1>YT) {

            return;

        }

        else {

            int xmin = max(XL, min(x1, x2));

            int xmax = min(XR, max(x1, x2));

            if (xmin <= xmax) {

                ansy1 = ansy2 = y1;

                ansx1 = xmin;

                ansx2 = xmax;

            }

            else {

                return;

            }

        }

    }

    //不平行于坐标轴

    else {

        float p1, p2, p3, p4;

        float q1, q2, q3, q4;

        p1 = -(x2 - x1);

        p2 = -p1;

        p3 = -(y2 - y1);

        p4 = -p3;

        q1 = x1 - XL;

        q2 = XR - x1;

        q3 = y1 - YB;

        q4 = YT - y1;

        float u1, u2, u3, u4;

        u1 = q1 / p1;

        u2 = q2 / p2;

        u3 = q3 / p3;

        u4 = q4 / p4;

        float umin, umax;

        if (p1 < 0) {

            if (p3 < 0) {

                umin = max(0, max(u1, u3));

                umax = min(1, min(u2, u4));

            }

            else {

                umin = max(0, max(u1, u4));

                umax = min(1, min(u2, u3));

            }

        }

        else {

            if (p3 < 0) {

                umin = max(0, max(u2, u3));

                umax = min(1, min(u1, u4));

            }

            else {

                umin = max(0, max(u2, u4));

                umax = min(1, min(u1, u3));

            }

        }

        if (umin <= umax) {

            ansx1 = x1 + umin * (x2 - x1);

            ansx2 = x1 + umax * (x2 - x1);

            ansy1 = y1 + umin * (y2 - y1);

            ansy2 = y1 + umax * (y2 - y1);

        }

        else {

            return;

        }

    }

    Displayline(ansx1, ansy1, ansx2, ansy2);

    return;

}

int main() {

    //用户定义a、b坐标

    float xa, ya, xb, yb;

    cout << "please input the coordinate of A point:" << endl;

    cin >> xa >> ya;

    cout << "please input the coordinate of B point:" << endl;

    cin >> xb >> yb;

    float x0, y0, x1, y1;

    //0->left bottom; 1->right top;

    initgraph(640,480);

    ExMessage m;

    //勾勒AB线段:绿色

    setlinecolor(GREEN);

    line(xa, ya, xb, yb);

    while (true) {

        m = getmessage(EX_MOUSE | EX_KEY);

        switch (m.message) {

        case WM_LBUTTONDOWN:

            x0 = m.x;

            y0 = m.y;

            setlinecolor(WHITE);

            setfillcolor(GREEN);

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

        case WM_RBUTTONDOWN:

            x1 = m.x;

            y1 = m.y;

            setlinecolor(WHITE);

            setfillcolor(GREEN);

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

            //判断用户是左上~右下 / 左下~右上 / 右上~左下 / 右下~左上

            if ((x0 < x1) && (y0 > y1)) {

                XL = x0;

                XR = x1;

                YB = y1;

                YT = y0;

            }

            else if ((x0 < x1) && (y0 < y1)) {

                XL = x0;

                XR = x1;

                YB = y0;

                YT = y1;

            }

            else if ((x0 > x1) && (y0 > y1)) {

                XL = x1;

                XR = x0;

                YB = y1;

                YT = y0;

            }

            else if ((x0 > x1) && (y0 < y1)) {

                XL = x1;

                XR = x0;

                YB = y0;

                YT = y1;

            }

            //勾勒裁剪框

            setlinecolor(RED);

            line(XL, YT, XR, YT);

            line(XL, YB, XR, YB);

            line(XL, YT, XL, YB);

            line(XR, YT, XR, YB);

            //裁剪部分为白色

            LiangBarsky(xa, ya, xb, yb, XL, XR, YT, YB);

        case WM_KEYDOWN:

            if (m.vkcode == VK_ESCAPE)

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

        }

    }

    closegraph();  //关闭绘图窗口

    return 0;

}

  • 8
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MorleyOlsen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值