【计算机图形学】裁剪算法(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;

}

1) 如果整条直线完全在窗口之内。此时,不需剪裁,显示整条直线,简称&ldquo;取&rdquo;之。 2) 如果整条直线明显在窗口之外,此时,不需剪裁,不显示直线。简称&ldquo;弃&rdquo;之。 3) 如果部分直线在窗口之内,部分在窗口之外。此时,需要求出直线与窗框之交点,并将窗口外的直线部分剪裁掉,显示窗口内的部分。 图2-1多边形裁剪区域编码 图2-2线段裁剪 为使计算机能够快速判断一条直线段与窗口属何种关系,采用如下编码方法。延长窗口的边,把未经裁剪的图形区域分成九个区,如图2-1所示。每个区具有一个四位代码,即四位二进制数,从左到右各位依次表示上、下、右、左。裁剪一条线段(如图2-2)时,先求出端点P1P2所在的区号code1和code2。若code1=0, 且code2=0,则说明线段P1P2均在窗口内,那么整条线段也必在窗口内,应取之。 若code1和code2经按位与运算后的结果code1&amp;code2&ne; 0,则说明两个端点同在窗口的上方、下方、左方或右方。在这种情况下,可判断线段完全在窗口外,可弃之。 如果上述两种条件均不成立,则按第三种情况处理。求出线段与窗口某边的交点,在交点处把线段一分为二,其中必有一段完全在窗口外,可弃之。再对另一段重复上述处理。 在实现本算法时,不必把线段与每条窗口边界依次求交,只要按顺序检测到端点的编码不为0,才把线段与对应的窗口边界求交。
MATLAB是一种强大的数学软件,常用于科学计算和工程应用。Liang-Barsky直线段裁剪算法是一种空间划分技术,用于检测两条或多条线段是否相交。这个算法主要用于计算机图形学中,特别是在处理复杂几何形状的碰撞检测场景。 在MATLAB中实现Liang-Barsky算法,你可以按照以下步骤进行: 1. **理解算法原理**:该算法通过判断每条线段上是否存在某个特定点使得它在另一条线上方或下方,并同时满足距离大于零的条件,以此判断线段间是否有交点。 2. **数据结构准备**:创建两个数组,一个存储所有线段的起点和终点坐标,另一个存储对应的斜率和截距。 3. **函数编写**: - 编写一个`liangBarskyIntersect`函数,接受两个线段作为输入(每个线段由起点、终点表示),然后逐个检查它们的交点情况。 - 函数内部会涉及一些数学运算,比如计算斜率、比较线段方向、以及检查临界点等。 4. **测试案例**:用一些已知的线段组合进行测试,验证函数的正确性。 5. **性能优化**:如果需要处理大量线段,可以考虑使用并行计算或循环展开等技巧提高效率。 下面是一个简单的MATLAB伪代码示例: ```matlab function result = liangBarskyIntersect(line1StartEnd, line2StartEnd) % 初始化结果变量 numSegments1 = length(line1StartEnd); numSegments2 = length(line2StartEnd); % ... (继续实现计算临界点、检查交叉条件的代码 ...) end % 示例使用 line1 = [0 0; 1 1]; % 线段AB line2 = [0 0; 0.5 0.75]; % 线段CD intersection = liangBarskyIntersect(line1, line2); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MorleyOlsen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值