【寒江雪】直线裁切算法

Cohen-Sutherland直线裁切算法

当一条直线横跨在平面内时,它的端点与裁切矩形的位置关系可以有9种情况。也就是说,矩形把平面分成了9个区域,端点可能分布在这九个区域中的一个

A

B

C

D

E

F

G

H

I

E区域即为裁切矩形区域

我们给点的坐标编个码,根据其在E区域的上边,下边,左边,右边来编码。因此需要四个二进制位。C[t]C[b][C[r]C[l]

对每个坐标进行解码,解码代码如下:

int CGraphicDlg::Encode(float x, float y, float XL, float XR, float YT, float YB)

{

    int c = 0;

    if (x < XL)c |= LEFT;

    if (x > XR)c |= RIGHT;

    if (y > YT)c |= TOP;

    if (y < YB)c |= BOTTOM;

    return c;

}

 

对于两个端点连成的线段。连个端点可能在E区域的同一侧,可能两个端点都在区域内,可能两个点横跨区域。

对于第一种情况,则舍弃这个线段

对于第二种情况,则直接画这条线

第三种情况,则需要另外判断

选取第三种情况来做详细说明
图片 

每一条直线段都优先考虑横坐标比较小的点。

对于图中几条曲线,当两个端点横跨在E区域之外时,可以求左边端点和E的左边界的交点,作为新的左端点,再解码,再与右端点的码值求与,会发现它们属于同一侧,舍弃之,其他没有横跨E又不同侧的情况都会经过求交点之后变为同侧,最后被舍弃

 

对于横跨E区域的曲线,先对左边端点进行求交解码,直到端点属于区域内侧。之后,再对又端点进行求交解码,直到端点属于区域内侧。这时只需要连线即可

 

对于右内到外的情况,则对不在E区域内的端点进行求交解码,直到其位于区域内。

 

完整代码如下,Cohen-Sutherland直线裁切算法是浮点数运算

int CGraphicDlg::Encode(float x, float y, float XL, float XR, float YT, float YB)

{

    int c = 0;

    if (x < XL)c |= LEFT;

    if (x > XR)c |= RIGHT;

    if (y > YT)c |= TOP;

    if (y < YB)c |= BOTTOM;

    return c;

}

VOID CGraphicDlg::CS_LineClip(float x1, float y1, float x2, float y2, float XL, float XR, float YT, float YB, COLORREF lineColor, int step, int pointSize)

{//直线段裁切

    int code1, code2;

    int code;

    int x;

    int y;

    if (x1 > x2) {

        swap(x1, x2);

        swap(y1, y2);

    }

    code1 = Encode(x1, y1,XL,XR,YT,YB);

    code2 = Encode(x2, y2, XL, XR, YT, YB);

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

        if ((code1&code2) != 0)return;

        if (code1 != 0)code = code1;

        else code = code2;

        if (LEFT&code) {

            x = XL;

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

        }

        else if (RIGHT&code) {

            x = XR;

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

        }

        else if (TOP&code) {

            y = YT;

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

        }

        else if (BOTTOM&code) {

            y = YB;

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

        }

 

        if (code1 == code) {

            x1 = x; y1 = y;

            code1 = Encode(x1, y1, XL, XR, YT, YB);

        }

        else {

            x2 = x; y2 = y;

            code2 = Encode(x2, y2, XL, XR, YT, YB);

        }

    }

    BresenhamInteger(x1,y1,x2,y2, lineColor, step, pointSize);

    return VOID();

}

 

 

中点直线裁切算法,与Cohen-Sutherland直线裁切算法类似,也是求交点的过程。只是求交的过程用的是二分法,这样一来,只需要整数运算,就可以寻找到交点。

同样,线段分为三种情况。

前两种一舍弃,一保留。第三种要经过处理

处理的条件应该是这样的

对两个端点跟别解码得code1,code2

如果code1code2求与不为0,该直线舍弃

否则,先处理左边端点。这时要用两个临时变量x,y存右边端点的坐标。

开始二分

二分条件如下:

1.    若中点坐标解码值与左边端点同侧,且与右边端点同侧,则舍弃该直线

2.    否则,若左边点坐标与中点坐标相同或者右边点坐标与中点坐标相同,则将左边点的坐标更新为x,y

3.    否则,若中点在E区域内,或者中点与x,y坐标的码值求与不为0,则更新x,y坐标为中点坐标

4.    否则,中点坐标在E区域外,且中点坐标码值与左边点的码值求与不为0,则更新左边点坐标为中点坐标

更新右边点的时候,只需要反过来看即可

 

对于条件2,需要特别说明,当中点坐标和左右某端点相等的时候,说明已经达到了极限。这时候要把待收缩的点更新到区域内(上边所说的左边点就是待更新的点,它永远在区域外)

完整代码如下:

 

int CGraphicDlg::Encode(int x, int y, int XL, int XR, int YT, int YB)

{

    int c = 0;

    if (x < XL)

        c |= LEFT;

    if (x > XR)

        c |= RIGHT;

    if (y > YT)

        c |= TOP;

    if (y < YB)

        c |= BOTTOM;

    return c;

}

 

VOID CGraphicDlg::MidLineClip(int x1, int y1, int x2, int y2, int XL, int XR, int YT, int YB, COLORREF lineColor, int step, int pointSize)

{//中点裁切算法

    int code1;

    int code2;

    int code;

    int code_mid;

    if (x1 > x2) {

        swap(x1, x2);

        swap(y1, y2);

    }

    code1 = Encode(x1, y1, XL, XR, YT, YB);

    code2 = Encode(x2, y2, XL, XR, YT, YB);

    int x_mid;

    int y_mid;

    int x;//"右"边点横坐标

    int y;//"左"边点横坐标

    if ((code1&code2) != 0)return;

    //第一步先找x1 y1与边界的交点

    if (code1 != 0) {

        x = x2;

        y = y2;

        code = code2;

        while (code1 != 0) {

            x_mid = (x + x1) >> 1;

            y_mid = (y + y1) >> 1;

 

            code_mid = Encode(x_mid, y_mid, XL, XR, YT, YB);

 

            if ((code&code_mid) != 0 &&(code1&code_mid) != 0) {//中点不在裁切矩形内且中点与两端点都同侧

                return;

            }

            else if ((x1 == x_mid&&y1 == y_mid) || (x == x_mid&&y == y_mid)) {//当求得的中点与x1 y1是同一点时,表明已经达到了极限该条件的优先级比以下两个高

                x1 = x;

                y1 = y;

                code1 = Encode(x1, y1, XL, XR, YT, YB);

            }

            else if (code_mid == 0|| (code_mid&code) != 0) {//中点在裁切矩形内 或者中点与点x,y同侧,则更新x,y为中点

                x = x_mid;

                y = y_mid;

                code = Encode(x, y, XL, XR, YT, YB);

            }

           

            else if ((code_mid & code1) != 0) {//中点不在裁切矩形内且 中点与x1 y1同侧,更新x1 y1

                x1 = x_mid;

                y1 = y_mid;

                code1 = Encode(x1, y1, XL, XR, YT, YB);

            }

        }

    }

    if (code2 != 0) {

        x = x1;

        y = y1;

        code = code1;

        while (code2 != 0) {

            x_mid = (x + x2) >> 1;

            y_mid = (y + y2) >> 1;

            code_mid = Encode(x_mid, y_mid, XL, XR, YT, YB);

            if ((code_mid&code) != 0 &&(code_mid&code2) != 0) {

                return;

            }

            else if ((x2 == x_mid&&y2 == y_mid) || (x == x_mid&&y == y_mid)) {

                x2 = x;

                y2 = y;

                code2 = Encode(x2, y2, XL, XR, YT, YB);

            }

            else if (code_mid == 0 || (code_mid&code) != 0) {

                x = x_mid;

                y = y_mid;

                code= Encode(x, y, XL, XR, YT, YB);

            }

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

                x2 = x_mid;

                y2 = y_mid;

                code2=Encode(x2, y2, XL, XR, YT, YB);

            }

        }

    }

 

    BresenhamInteger(x1, y1, x2, y2, lineColor, step, pointSize);

    //第二步找x2 y2与边界的交点

    return VOID();

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值