Cohen-Sutherland直线裁切算法 当一条直线横跨在平面内时,它的端点与裁切矩形的位置关系可以有9种情况。也就是说,矩形把平面分成了9个区域,端点可能分布在这九个区域中的一个
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 如果code1和code2求与不为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(); } ![]() |