一、算法实现原理
Cohen-Sutherland直线段剪裁算法:
算法原理:
(1)判断线段两端是否都落在窗口内,如果是,则线段完全可见,否则进行下一步
(2)判断线段两端是否都落在窗口外,如果是,则线段完全不可见,否则进行下一步
(3)求线段与窗口延长线的交点,这个交点将线段分为两端,一段完全不可见丢弃,对剩下的一部分重新进行上述一、二操作,直到结束。
算法实现:
用窗口四条边所在的直线将整个二位平面分为9个区域,定义下界限为ymin,上界限为ymax,左界限为xmin,右界限为xmax,按照以下规则进行编码
Code = C上C下C右C左。进行编码
对于线段的端点,我们只需要按照这种规则定义对于的编码,即可表示端点相对窗口所在位置。
对于原理里的(1),只需要判断两个Code是否都为0
对于原理里的(2),既两个端点都在窗口的同一侧,通过将两个端点的code相与,只需要判断是否为1
对于原理里的(3),首先判断哪个点在窗口外(如果两端都在窗口外,则为默认端点),然后线段与窗口求交,再将窗口外端点改为交点,重新编码。
重复上述步骤即可实现。
梁友栋-Barsky算法实现:
算法原理:
通过将线段的两个端点以及线段延长线L与坐标轴的交点,转换到一个一维对象来处理,这样记L与窗口的交点为Q0,Q1,剪裁线段为P0,P1。
此时这四个点就会有4种排序情况:Q0 P0 P1 Q1(完全在内部);P0 Q0 Q1 P1(有两个交点);Q0 Q1 P0 P1、P0 P1 Q0 Q1(完全在外面);P0 Q0 P1 Q1、Q0 P0 Q1 P1(有一个交点)。梁友栋-Barsky算法就只需要将这几种情况,并且得到对应四个裁剪的参数就行。
算法实现:
这时我们令d为线段起始点(x0,y0)与终点(x1,y1)分别相减所得,q为起始点到窗口的距离/线段总长度。随后通过下面几种方式进行判断。
①如果q=0,d<0,说明直线与裁剪框平行,并且位于裁剪框的外面,直线为不可见,可抛弃,直接结束
d>=0,说明直线在它所平行的窗口边界的内部,还需进一步计算确定直线是否在窗口内、外、或者相交
②如果q<0,则说明是以xmin为起始边(剪左),令d为x0-xL,如果d<t0(x0在xL左边)并且d/q>=t1,则说明x1也在xl左边,直接结束;如果d<0(x0在xL左边)并且d/q>t0,则说明x1在xL右边,进行裁剪;如果d>0,则说明在右边,不要处理。
③如果q>0,则说明是以xmax为起始边(剪右),令d为xR-x0,如果d>0(x0在xR右边)并且d/q<t0,则说明x1也在xR右边,直接结束;如果d>0(x0在xR右边)并且d/q<t1,则说明x1在xR左边,进行裁剪;如果d<0,则说明在左边,不要处理。
如果当线段要裁剪另一边时,只需要将q取反,d为xR-x0,在③中算法过一遍。采用y轴两边对称原理,镜像成相反的数据,就可以按照另一方向处理。
y的裁剪同理。
二、代码实现
设计矩形框和待剪裁直线段:
//100x100矩阵
Rect rect;
rect.bottom = -50;
rect.top = 50;
rect.left = -50;
rect.right = 50;
//平行于x轴线段,有两个交点
lian(-100, 35, 100, 35, rect, pDC, RGB(12, 45, 209));
//(x0,y0)点在内部右边,(x1,y1)点在外面左边
lian(25, 10, -60, -90, rect, pDC, RGB(12, 45, 209));
//全在里面
lian(30, -25, -45, 45, rect, pDC, RGB(12, 45, 209));
//全在外面
lian(-60, -60, -100, 700, rect, pDC, RGB(12, 45, 209));
//平行于x轴线段,有两个交点
/*CohenSutherlandLineClip(-100, 35, 100, 35, rect, pDC, RGB(209, 12, 52));
//(x0,y0)点在内部右边,(x1,y1)点在外面左边
CohenSutherlandLineClip(25, 10, -60, -90, rect, pDC, RGB(209, 12, 52));
//全在里面
CohenSutherlandLineClip(30, -25, -45, 45, rect, pDC, RGB(209, 12, 52));
//全在外面
CohenSutherlandLineClip(-60, -60, -100, 700, rect, pDC, RGB(209, 12, 52));*/
Cohen-Sutherland直线段剪裁算法:
void CLianbarsketView::CohenSutherlandLineClip(float x0, float y0, float x1, float y1, const Rect rect,CDC *pDC,int color)
{
BOOL accept, done;
OutCode outCode0, outCode1, * outCodeOut;
float x, y;
accept = FALSE;
done = FALSE;
CompOutCode(x0, y0, rect, &outCode0);
CompOutCode(x1, y1, rect, &outCode1);
do
{
if (outCode0.all==0&&outCode1.all==0)
{
accept = TRUE;
done = TRUE;
}
else
if ((outCode0.all & outCode1.all) != 0)
done = TRUE;
else
{
if (outCode0.all != 0)
outCodeOut = &outCode0;
else
outCodeOut = &outCode1;
if (outCodeOut->left)
{
y = y0 + (y1 - y0) * ((float)(rect.left) - x0) / (x1 - x0);
x = (float)rect.left;
}
else
if (outCodeOut->top)
{
x = x0 + (x1 - x0) * ((float)(rect.top) - y0) / (y1 - y0);
y = (float)rect.top;
}
else
if (outCodeOut->right)
{
y = y0 + (y1 - y0) * ((float)(rect.right) - x0) / (x1 - x0);
x = (float)rect.right;
}
else
{
x = x0 + (x1 - x0) * ((float)(rect.bottom) - y0) / (y1 - y0);
y = (float)rect.bottom;
}
if (outCodeOut->all == outCode0.all)
{
x0 = x;
y0 = y;
CompOutCode(x0, y0, rect, &outCode0);
}
else
{
x1 = x;
y1 = y;
CompOutCode(x1, y1, rect, &outCode1);
}
}
} while (!done);
if (accept)
{
IsAccept = TRUE;
DDALine(pDC, (int)(x0 + 0.5), (int)(y0 + 0.5), (int)(x1 + 0.5), (int)(y1 + 0.5), color);
}
}
void CLianbarsketView::CompOutCode(float x, float y, const Rect rect, OutCode* outCode)//计算点(x,y)的编码
{
outCode->all = 0;
outCode->top = outCode->bottom = 0;
if (y > (float)(rect.top))
{
outCode->top = 1;
outCode->all += 8;
}
else
if (y < (float)(rect.bottom))
{
outCode->bottom = 1;
outCode->all += 4;
}
outCode->right = outCode->left = 0;
if (x > (float)(rect.right))
{
outCode->right = 1;
outCode->all += 2;
}
else
if (x < (float)(rect.left))
{
outCode->left = 1;
outCode->all += 1;
}
}
梁友栋-Barsky算法实现:
void CLianbarsketView::lian(float x0, float y0, float x1, float y1, const Rect rect, CDC* pDC, int color) {
float delatx, delaty, t0, t1;
delatx = x1 - x0;
t0 = 0; t1 = 1;
if(CT(-delatx,x0-rect.left,&t0,&t1))
if (CT(delatx, rect.right-x0, &t0, &t1)) {
delaty = y1 - y0;
if (CT(-delaty,y0-rect.bottom, &t0, &t1))
if (CT(delaty,rect.top-y0, &t0, &t1)){
DDALine(pDC,(int)(x0 + t0 * delatx),(int)(y0 + t0 * delaty), (int)(x0+t1*delatx), (int)(y0+t1*delaty), color);
}
}
}
bool CLianbarsketView::CT(float q, float d, float* t0, float* t1) {
if (q < 0){
float tmp = d / q;
if (tmp>*t1) {
return false;
}
else if(tmp>*t0){
*t0 = tmp;
return true;
}
}
else if(q > 0) {
float tmp = d / q;
if (tmp <*t0){
return false;
}
else if (tmp <*t1) {
*t1 = tmp;
return true;
}
}
else if (d < 0)
return false;
return true;
}
三、算法实现结果
Cohen-Sutherland直线段剪裁算法结果:
梁友栋-Barsky算法裁剪结果: