计算机图形学:Cohen-Sutherland直线段剪裁算法及梁友栋-Barsky裁剪算法(算法原理及代码实现)

一、算法实现原理

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算法裁剪结果

  • 10
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值