实验目的:
- 理解裁剪的作用
- 掌握直线裁剪的算法
实验内容:
一、编写程序实现Cohen Sutherland裁剪算法
算法原理:
首先判断直线段是否全部在窗口内,是,则保留;不是,则再判断是否完全在窗口之外,如是,则舍弃。
如果这两种情况都不属于,则将此直线段从交点处分割,对分割后的线段再进行如前判断。
直至所有直线段和由直线段分割出来的子线段都已经确定了是保留还是舍弃为止。
算法流程: - 对直线的两个端点进行编码
编码方式
注意:l为left,r为right,b为bottom,t为top
(1)若x小于wxl,D0=1,否则D0=0 C1 | 0001 按位或
(2)若x大于wxr,D1=1,否则D1=0 C1 | 0010
(3)若y小于wyb,D2=1,否则D2=0 C1 | 0100
(4)若y大于wyt,D3=1,否则D3=0 C1 | 1000
- 进行区域测试
如果code1和code2均为0,code1按位或code2,则说明P1和P2均在窗口内,那么线段全部位于窗口内部,应取之。
如果code1和code2经过按位与运算后的结果code1&code2不等于0,说明P1和P2同时在窗口的上方、下方、左方或右方,那么线段全部位于窗口的外部,应弃之。 - 当2不满足时,确保p1在窗口外部:若p1在窗口内,则交换p1和p2的坐标值和编码。
- 求出直线段与窗口边界的交点,并用该交点的坐标值替换p1的坐标值。也即在交点s处把线段一分为二。考虑到p1是窗口外的一点,因此可以去掉p1s。转(2)。
- 画出当前的直线段p1p2。
- 算法结束。
实验作业:
- 假设裁剪窗口参数为wxl=250;wxr=850;wyb=250;wyt=450;其中,wxl为左侧裁剪边,wxr为右侧裁剪边,wyb为下侧裁剪边,wyt为上裁剪边。
编写unsigned int EnCode(double,double);函数,实现对直线端点的编码。
提示:编码的代码实现区域编码思路:如,端点在裁剪窗口左侧,其编码最后一位为1,可以按位或0001,将编码最后一位至1.
代码:
unsigned int CCohenSutherlandView::EnCode(CPoint point)
{
int C1=0;
if (point.x<wxl)
C1=C1|D0;
if (point.x>wxr)
C1=C1|D1;
if (point.y<wyb)
C1=C1|D2;
if (point.y>wyt)
C1=C1|D3;
return C1;
}
- 写出裁剪函数CohenSutherland(),并在OnDraw()里面绘制出裁剪窗口,绘制出裁剪后的直线,测试裁剪结果是否正确,给出至少三种情况的直线的裁剪,第一种情况,全部保留,第二种情况,完全去除,第三种情况,直线需要剪切。
代码:
在CohenSutherlandView.h中:
public:
int wxl,wxr,wyb,wyt;
unsigned int CCohenSutherlandView::EnCode(CPoint point);
void CCohenSutherlandView::CohenSutherland(CPoint p1,CPoint p2,CDC *pdc);
void CCohenSutherlandView::DDA(CPoint p1,CPoint p2,CDC *pdc,COLORREF color);
// 重写
在CohenSutherlandView.cpp中:
#define D0 1 //0001
#define D1 2 //0010
#define D2 4 //0100
#define D3 8 //1000
void CCohenSutherlandView::OnDraw(CDC* pDC)
{
CCohenSutherlandDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
wxl=250,wxr=850,wyb=250,wyt=450;
//CPoint X(250,250);//左上
// CPoint Y(850,450);//右下
pDC->Rectangle(wxl,wyb,wxr,wyt);
CPoint A(500,400),B(800,420);//全部保留
CohenSutherland(A,B,pDC);
CPoint C(1000,500),D(900,500);//完全去除
CohenSutherland(C,D,pDC);
CPoint E(350,300),F(1200,300);//直线需要剪切
CohenSutherland(E,F,pDC);
// pDC->Polyline()
// TODO: 在此处为本机数据添加绘制代码
}
void CCohenSutherlandView::CohenSutherland(CPoint p1,CPoint p2,CDC *pdc)
{
int code1,code2,code;
CPoint p;
code1=EnCode(p1);
code2=EnCode(p2);
while (code1!=0||code2!=0)
{
if (code1&&code2!=0)//全部位于外部
return;
if(code1!=0)code=code1;//部分在外部
else
code=code2;
if ((D0&code)!=0)//Left
{
p.x=wxl;
p.y=p1.y+(p.x-p1.x)*(p2.y-p1.y)/(p2.x-p1.x);
}
else if ((D1&code)!=0)//Right
{
p.x=wxr;
p.y=p1.y+(p.x-p1.x)*(p2.y-p1.y)/(p2.x-p1.x);
}
else if ((D2&code)!=0)//Bottom
{
p.y=wyb;
p.x=p1.x+(p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y);
}
else if ((D3&code)!=0)//Top
{
p.y=wyt;
p.x=p1.x+(p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y);
}
if(code==code1)//p1在外部
{
p1.x=p.x;p1.y=p.y;
code1=EnCode(p);
}
else
{
p2.x=p.x;p2.y=p.y;
code2=EnCode(p);
}
}
CPen pen;
pen.CreatePen(PS_SOLID,5,RGB(0,255,255));
pdc->SelectObject(&pen);
pdc->MoveTo(p1);
pdc->LineTo(p2);
}
截图: