CS直线裁剪算法

CS直线裁剪算法,第1张

#include
#include
#include
using namespace std;
//分别用四个符号常量记录四个边界的编号,相当于C语言中的define宏定义
const unsigned TOP = 8;
const unsigned BOTTOM = 4;
const unsigned RIGHT = 2;
const unsigned LEFT = 1;

static unsigned xl, yb, xr, yt;

//优化后的Bresenham算法,能够处理斜率不存在,斜率为负数,斜率绝对值大于一等情况,并且可以根据需要自动调整两点顺序,具有良好的通用性
//使用只需要输入两点的横纵坐标值即可
void Bresenham(const unsigned& x1, const unsigned& y1, const unsigned& x2, const unsigned& y2)
{
	int dx = x2 - x1, dy = y2 - y1;
	enum MainChangeDirection { x, y };//用一个枚举变量记录直线的主要变化方向(x方向或者y方向)(用枚举变量更加符合语义)
	MainChangeDirection Direction;
	if (abs(dx) >= abs(dy))
		Direction = x;
	else Direction = y;
	if ((Direction == x && x1 > x2) || (Direction == y && y1 > y2))//如果在主要变化方向上的次序相反了,则递归调用自身即可(相当于交换两个点)
	{
		Bresenham(x2, y2, x1, y1);
		return;
	}

	if (dx == 0)//处理斜率不存在的特殊情况
	{
		for (unsigned y = y1; y <= y2; ++y)
		{
			putpixel(x1, y, YELLOW);
		}
		return;
	}

	//对于不同的变化方向以及斜率的正负不同,需要使用不同的数学表达式(自行推导出),也就是需要分情况
	double k = double(dy) / dx;
	if (Direction == x)//处理主要变化方向为x轴的情况
	{
		unsigned x = x1, y = y1;
		if (k >= 0)//主要变化方向为x轴并且斜率大于等于零
		{
			int e = -dx;
			for (; x <= x2; ++x)
			{
				putpixel(x, y, YELLOW);
				e += 2 * dy;
				if (e > 0)
				{
					y++;
					e -= 2 * dx;
				}
			}
		}
		else if (k < 0)//主要变化方向为x轴且斜率小于零
		{
			int e = dx;
			for (; x <= x2; ++x)
			{
				putpixel(x, y, YELLOW);
				e += 2 * dy;
				if (e <= 0)
				{
					y--;
					e += 2 * dx;
				}
			}
		}
	}
	else if (Direction == y)
	{
		unsigned y = y1, x = x1;
		if (k >= 0)//主要变化方向为y轴方向并且斜率大于等于零
		{
			int e = -dy;
			for (; y <= y2; ++y)
			{
				putpixel(x, y, YELLOW);
				e += 2 * dx;
				if (e > 0)
				{
					x++;
					e -= 2 * dy;
				}
			}
		}
		else if (k < 0)//主要变化方向为y轴方向并且斜率小于零
		{
			int e = dy;
			for (; y <= y2; ++y)
			{
				putpixel(x, y, YELLOW);
				e += 2 * dx;
				if (e <= 0)
				{
					x--;
					e += 2 * dy;
				}
			}
		}
	}
	return;
}

unsigned short Region_Coding(const unsigned& x, const unsigned& y)
{
	short coding = 0;
	if (y > yt)
		coding += 8;
	else if (y < yb)
		coding += 4;
	if (x > xr)
		coding += 2;
	else if (x < xl)
		coding += 1;
	return coding;
}

void CS_LineClip(const unsigned& x1, const unsigned& y1, const unsigned& x2, const unsigned& y2)
{
	unsigned p1, p2;
	//通过区域码函数分别求出两个点的区域码
	p1 = Region_Coding(x1, y1);
	p2 = Region_Coding(x2, y2);

	//第一种情况:两点的区域码均在窗口内部,则直接画出该直线即可
	if (p1 == 0 && p2 == 0)
	{
		Bresenham(x1, y1, x2, y2);
		return;
	}
	//第二种情况,两点的区域码均不在窗口内部,则不绘制任何直线
	else if ((p1 & p2) != 0)
	{
		return;
	}
	//第三种情况:即排除之前两种情况后的剩余情况,此时进行分类讨论
	else
	{
		//首先处理斜率不存在的特殊情况,该情况下可以直接画出线
		if ((x1 == x2 && y1 <= yb && y2 >= yt) || (x1 == x2 && y1 >= yt && y2 <= yb))
		{
			Bresenham(x1, yb, x2, yt);
			return;
		}
		//求出直线的斜率。


注意:此处一定要用到类型转换!否则求出的斜率很可能存在异常 double k = (double(y2) - double(y1)) / (double(x2) - double(x1)); if ((p1 & LEFT) != 0)//由于已经使得两点的横坐标升序排列,因此如果左边点在左边界的左边的话,则求出直线与左边界的交点,以交点代替第一个点递归执行函数 { unsigned New_y1 = unsigned(k * (xl - x1) + y1 + 0.5); CS_LineClip(xl, New_y1, x2, y2); return; } if ((p2 & RIGHT) != 0)//运行到此处时直线左端点一定在左边界上或左边界的右边,此时对右端点进行类似检查:如果右端点在右边界的右边,则取直线与右边界的交点。


{ unsigned New_y2 = unsigned(k * (xr - x1) + y1 + 0.5); CS_LineClip(x1, y1, xr, New_y2); return; } //剩下的情况中只包含两点横坐标均在左右边界之间,但是纵坐标不在上下边界内的情况,需要分斜率正负进行讨论 if (k >= 0) { if ((p2 & TOP) != 0)//斜率为正且右边的点在上边界以上时,求出直线与上边界的交点来代替上点,递归调用自身即可 { unsigned New_x2 = unsigned(x1 + (yt - y1) / k + 0.5); CS_LineClip(x1, y1, New_x2, yt); return; } if ((p1 & BOTTOM) != 0)//斜率为正且左边的点在下边界以下时,求出直线与下边界的交点来代替下点,递归调用自身 { unsigned New_x1 = unsigned(x1 + (yb - y1) / k + 0.5); CS_LineClip(New_x1, yb, x2, y2); return; } } if (k < 0)//斜率小于零的情况进行类似讨论即可 { if ((p1 & TOP) != 0) { unsigned New_x1 = unsigned(x1 + (yt - y1) / k + 0.5); CS_LineClip(New_x1, yt, x2, y2); return; } if ((p2 & BOTTOM) != 0) { unsigned New_x2 = unsigned(x1 + (yb - y1) / k + 0.5); CS_LineClip(x1, y1, New_x2, yb); return; } } } } int main(void) { int x1, y1, x2, y2; cout << "请分别输入原始直线两端点的横纵坐标:"; cin >> x1 >> y1 >> x2 >> y2; cout << "请分别输入裁剪窗口左下角和右上角的横纵坐标:"; cin >> xl >> yb >> xr >> yt; cout << "原始直线和裁剪窗口如下图所示:(按任意键查看,查看后按任意键裁剪)"; _getch(); initgraph(800, 640); Bresenham(x1, y1, x2, y2);//通过之前学过的Bresenham方法进行画线 *** 作 rectangle(xl, yt, xr, yb); _getch(); closegraph(); initgraph(800, 640); CS_LineClip(x1, y1, x2, y2); rectangle(xl, yt, xr, yb); _getch(); closegraph(); return 0; }

备注:①graphics.h包需要自行百度免费下载 ②代码中的Bresenham画线算法可以用任意画直线的函数代替,对结果不产生影响。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/579175.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存