一、实验目的:
直线段的裁剪:编码裁剪算法,中点分割裁剪算法。
二、实验内容:
//BasicGraph.cpp
//请将下列裁剪程序补充完整,并用注释说明是何种裁剪算法
void Encode (int x,int y,int *code,int XL,int XR,int YB,int YT)
{
//请将此程序补充完整
int c=0;
if(x<XL) c=c|LEFT;
else if(x>XR) c=c|RIGHT;
if(y<YB) c=c|BOTTOM;
else if(y>YT) c=c|TOP;
(*code)=c;
}
//编码裁剪算法:
void C_S_Line(POINT &p1,POINT &p2,int XL,int XR,int YB,int YT)
{
//请将此程序补充完整
int x1,x2,y1,y2,x,y,code1,code2,code;
x1=p1.x; x2=p2.x; y1=p1.y; y2=p2.y;
Encode(x1,y1,&code1,XL,XR,YB,YT);
Encode(x2,y2,&code2,XL,XR,YB,YT);
while(code1!=0||code2!=0)
{
if((code1&code2)!=0) return;
code=code1;
if(code1==0) code=code2;
if((LEFT&code)!=0)
{x=XL;y=y1+(y2-y1)*(XL-x1)/(x2-x1);}
else if((RIGHT&code)!=0)
{x=XR;y=y1+(y2-y1)*(XR-x1)/(x2-x1);}
if((BOTTOM&code)!=0)
{y=YB;x=x1+(x2-x1)*(YB-y1)/(y2-y1);}
else if((TOP&code)!=0)
{y=YT;x=x1+(x2-x1)*(YT-y1)/(y2-y1);}
if(code==code1)
{x1=x;y1=y;Encode(x,y,&code1,XL,XR,YB,YT);}
else
{x2=x;y2=y;Encode(x,y,&code2,XL,XR,YB,YT);}
}
p1.x=x1;p1.y=y1;p2.x=x2;p2.y=y2;
}
int IsInArea(POINT point,int XL,int XR,int YB,int YT)
{
//请将此程序补充完整
if(point.x>=XL && point.x<=XR && point.y>YB && point.y<YT) return 1;
else return 0;
}
int NotIntersect(POINT begin,POINT end,int XL,int XR,int YB,int YT)
{
//请将此程序补充完整
int maxx,maxy,minx,miny;
maxx=(begin.x>end.x)?begin.x:end.x;
minx=(begin.x<end.x)?begin.x:end.x;
maxy=(begin.y>end.y)?begin.y:end.y;
miny=(begin.y<end.y)?begin.y:end.y;
if(maxx<XL|| minx>XR||maxy<YB||miny>YT) return 1;
else return 0;
}
//中点裁剪算法:
POINT ClipMid(POINT begin,POINT end,int XL,int XR,int YB,int YT)
{
//请将此程序补充完整
POINT mid,temp;
if(IsInArea(begin,XL,XR,YB,YT)) temp=begin;
else if(NotIntersect(begin,end,XL,XR,YB,YT)) temp=begin;
else
{
mid.x=(begin.x+end.x)/2;mid.y=(begin.y+end.y)/2;
if(abs(mid.x-end.x)<=1&& abs(mid.y-end.y)<=1) temp=mid;
else
{
if(NotIntersect(begin,mid,XL,XR,YB,YT))
temp=ClipMid(mid,end,XL,XR,YB,YT);
else
temp=ClipMid(begin,mid,XL,XR,YB,YT);
}
}
return temp;
}
//Liang-Barsky直线裁剪算法:
void ClipParameter(POINT &p1,POINT &p2,int XL,int XR,int YB,int YT)
{
float u1=0.0,u2=1.0;
float dx=p2.x-p1.x,dy=p2.y-p1.y;
if(clipTest(-dx,p1.x-XL,&u1,&u2))
if(clipTest(dx,XR-p1.x,&u1,&u2))
if(clipTest(-dy,p1.y-YB,&u1,&u2))
if(clipTest(dy,YT-p1.y,&u1,&u2))
{
if(u2<1.0)
{
p2.x=p1.x+u2*dx;
p2.y=p1.y+u2*dy;
}
if(u1>0.0)
{
p1.x=p1.x+u1*dx;
p1.y=p1.y+u1*dy;
}
}
}
int clipTest(float p,float q,float *u1,float *u2)
{
float r;
int remainFlag=1;
if(p<0.0)
{
r=q/p;
if(r>*u2) remainFlag=0;
else if(r>*u1) *u1=r;
}
else if(p>0.0)
{
r=q/p;
if(r<*u1) remainFlag=0;
else if(r<*u2) *u2=r;
}
else //*p=0
if(q<0.0) remainFlag=0;
return remainFlag;
}
//逐边裁剪算法:
//typedef struct tRes { int yes,isIn; POINT pout;} Res;
Res TestIntersect(int edge,int type,POINT p1,POINT p2)
{//判断p2是否在所裁剪的窗边edge的内侧,是否与p1点分别在窗边edge的异侧
float dx,dy,m;
Res res;int isIn=0,yes=0;POINT pout;
dy=p2.y-p1.y;dx=p2.x-p1.x;m=dy/dx;
switch(type)
{
case 1: /*right*/
if(p2.x<=edge){isIn=1;if(p1.x>edge)yes=1;}
else if(p1.x<=edge)yes=1;break;
case 2: /*bottom*/
if(p2.y>=edge){isIn=1;if(p1.y<edge)yes=1;}
else if(p1.y>=edge)yes=1;break;
case 3: /*left*/
if(p2.x>=edge){isIn=1;if(p1.x<edge)yes=1;}
else if(p1.x>=edge)yes=1;break;
case 4: /*top*/
if(p2.y<=edge){isIn=1;if(p1.y>edge)yes=1;}
else if(p1.y<=edge)yes=1;
default: break;
}
if(yes)
{
if((type==1) || (type==3))
{ pout.x=edge;pout.y=p1.y+m*(pout.x-p1.x);}
if((type==2) || (type==4))
{ pout.y=edge;pout.x=p1.x+(pout.y-p1.y)/m;}
}
res.isIn=isIn;res.yes=yes;res.pout=pout;
return res;
}
int clipSingleEdge(int edge,int type,int nin,POINT pin[50],POINT pout[50])
/*对多边形pin与窗边edge进行裁剪,返回裁剪后的多边形pout及点数*/
{
int i,k=0;POINT p;Res res;
p.x=pin[nin-1].x;p.y=pin[nin-1].y;
for(i=0;i<nin;i++)
{
res=TestIntersect(edge,type,p,pin[i]);
if(res.yes)
{ pout[k].x=res.pout.x;pout[k].y=res.pout.y;k++;}
if(res.isIn)
{ pout[k].x=pin[i].x;pout[k].y=pin[i].y;k++;}
p.x=pin[i].x;p.y=pin[i].y;
}
return k;
}
void ClipEdgePolygon(POINT ps[50],int &n,int XL,int XR,int YB,int YT)
{ /*对多边形ps进行逐边裁剪*/
int n1=0,n2=0;
POINT pt[50];
n1=clipSingleEdge(XR,1,n,ps,pt);
n2=clipSingleEdge(YB,2,n1,pt,ps);
n1=clipSingleEdge(XL,3,n2,ps,pt);
n2=clipSingleEdge(YT,4,n1,pt,ps);
n=n2;
}
//多边形编码裁剪算法:
void ClipEncodePolygon(POINT ps[50],int &n,int XL,int XR,int YB,int YT)
{
POINT tp[50];
int k=0,m;
int code1,code2,code;
int x,y;
for(int i=0;i<n-1;i++)
{
Encode(ps[i].x,ps[i].y,&code1,XL,XR,YB,YT);
Encode(ps[i+1].x,ps[i+1].y,&code2,XL,XR,YB,YT);
code=code1;m=i;
for(int j=0;j<2;j++)
{
if((code1 & code2)!=0) //线段两端都在窗口外的同一侧
{
switch(code)
{
case 1:x=XL;y=ps[m].y;break;
case 2:x=XR;y=ps[m].y;break;
case 4:x=ps[m].x;y=YB;break;
case 5:x=XL;y=YB;break;
case 6:x=XR;y=YB;break;
case 8:x=ps[m].x;y=YT;break;
case 9:x=XL;y=YT;break;
case 10:x=XR;y=YT;break;
}
tp[k].x=x;tp[k].y=y;k++;
}
else if((code1 & code2)==0) //线段两端不在窗口的同一侧
{
if(code==0)
{
tp[k]=ps[m];k++;
}
else if ((LEFT & code) !=0) //线段与左边界相交
{
x=XL;
y=ps[i].y+(ps[i+1].y-ps[i].y)*(XL-ps[i].x)/(ps[i+1].x-ps[i].x);
if(y>YB && y<YT){tp[k].x=x;tp[k].y=y;k++;}
}
else if((TOP & code)!=0) //线段与上边界相交
{
y=YT;
x=ps[i].x+(ps[i+1].x-ps[i].x)*(YT-ps[i].y)/(ps[i+1].y-ps[i].y);
if(x>XL && x<XR){tp[k].x=x;tp[k].y=y;k++;}
}
else if((RIGHT & code)!=0) //线段与右边界相交
{
x=XR;
y=ps[i].y+(ps[i+1].y-ps[i].y)*(XR-ps[i].x)/(ps[i+1].x-ps[i].x);
if(y>YB && y<YT){tp[k].x=x;tp[k].y=y;k++;}
}
else if((BOTTOM & code) != 0) //线段与下边界相交
{
y=YB;
x=ps[i].x+(ps[i+1].x-ps[i].x)*(YB-ps[i].y)/(ps[i+1].y-ps[i].y);
if(x>XL && x<XR){tp[k].x=x;tp[k].y=y;k++;}
}
}
code=code2;m++;
}//for(j)
}//for(i)
for(i=0;i<k;i++)
ps[i]=tp[i];
n=k;
}
//函数的调用,裁剪窗口的调整
//DrawView.cpp文件
//裁剪窗口的调整
CDrawView::CDrawView()
{
/************请在此函数中将裁剪窗口大小调整为长度100单位像素,宽度50单位像素的矩形********/
// TODO: add construction code here
//
m_pWidth=1;
m_pStyle=PEN_STYLE_SOLID;
m_pColor=RGB(0,0,0);
m_FFlag=0;
m_FColor=RGB(0,0,0);
m_HFlag=0;
CurrentDraw=DRAW_VCLINE;
m_Num=0;
m_Drag=0;
m_HCursor=AfxGetApp()->LoadStandardCursor(IDC_CROSS);
//
DrawType=0;
ClipFlag=0;
ClipType=-1;
XL=200;XR=300;YB=150;YT=200;
//XL=200;XR=500;YB=150;YT=400;
ClipWindowColor=RGB(192,192,50);
}
void CDrawView::OnDraw(CDC* pDC)
{
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if(ClipFlag)
{
CPen NewPen,*pOldPen;
NewPen.CreatePen(PS_DASH,1,ClipWindowColor);
pOldPen=pDC->SelectObject(&NewPen);
pDC->MoveTo(XL,YB);
pDC->LineTo(XR,YB);
pDC->LineTo(XR,YT);
pDC->LineTo(XL,YT);
pDC->LineTo(XL,YB);
}
int index;
index=pDoc->GetShapeNumber();
for(int i=0;i<index;i++)
pDoc->GetShape(i)->Drawing(pDC);
}
void CDrawView::OnInitialUpdate()
{
CSize sizeTotal;
sizeTotal.cx = 640; sizeTotal.cy = 480;
SetScrollSizes(MM_TEXT, sizeTotal);
// TODO: Add your specialized code here and/or call the base class
}
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
OnPrepareDC(&dc);
dc.DPtoLP(&point);
m_pPrev=point;
m_pOrigin=point; //点击鼠标左键作为拖动绘图的第一点
m_Drag=1;
SetCapture();
RECT rect;
GetClientRect(&rect);
ClientToScreen(&rect);
ClipCursor(&rect);
CScrollView::OnLButtonDown(nFlags, point);
}
//函数调用处
void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(m_Drag)
{
m_Drag=0;
ReleaseCapture();
ClipCursor(NULL);
CDrawDoc *pDoc=GetDocument();
CShape *pShape;
POINT p1,p2;
if(CurrentDraw==DRAW_VCLINE || CurrentDraw==DRAW_DDALINE ||
CurrentDraw==DRAW_MIDLINE || CurrentDraw==DRAW_BSHLINE)
{
if(ClipFlag)
{
switch(ClipType)
{
/****************编码裁剪函数调用处*************/
case CLIP_ENCODE:C_S_Line(m_pOrigin,m_pPrev,XL,XR,YB,YT); break;
/****************中点分割裁剪函数调用处************/
case CLIP_MIDPOINT: ClipMid(m_pPrev,m_pOrigin,XL,XR,YB,YT);
p1=ClipMid(m_pPrev,m_pOrigin,XL,XR,YB,YT);
p2=ClipMid(m_pOrigin,m_pPrev,XL,XR,YB,YT);
m_pOrigin=p1;m_pPrev=p2;
break;
case CLIP_PARAMETER:ClipParameter(m_pOrigin,m_pPrev,XL,XR,YB,YT);
break;
}
}
pShape=new CLine(m_pOrigin,m_pPrev,m_pWidth,m_pStyle,m_pColor,DrawType);
pDoc->AddShape(pShape);
}
if(CurrentDraw==DRAW_RECTANGLE)
{
if(ClipType==CLIP_WINDOW)
{
XL=m_pOrigin.x;XR=m_pPrev.x;
YB=m_pOrigin.y;YT=m_pPrev.y;
}
else
{
pShape=new CRectangle(m_pOrigin,m_pPrev,m_pWidth,m_pStyle,m_pColor,
m_FFlag,m_FColor,m_HFlag,m_Hatch);
pDoc->AddShape(pShape);
}
}
if( CurrentDraw==DRAW_VCCIRCLE || CurrentDraw==DRAW_MIDCIRCLE ||
CurrentDraw==DRAW_BSHCIRCLE)
{
pShape=new CCircle(m_pOrigin,m_pPrev,m_pWidth,m_pStyle,m_pColor,
m_FFlag,m_FColor,m_HFlag,m_Hatch,DrawType);
pDoc->AddShape(pShape);
}
if(CurrentDraw==DRAW_VCELLIPSE || CurrentDraw==DRAW_MIDELLIPSE)
{
pShape=new CEllipse(m_pOrigin,m_pPrev,m_pWidth,m_pStyle,m_pColor,
m_FFlag,m_FColor,m_HFlag,m_Hatch,DrawType);
pDoc->AddShape(pShape);
}
pDoc->UpdateAllViews(NULL);
}
CScrollView::OnLButtonUp(nFlags, point);
}
三实验结果:
四、实验总结
通过这次试验使我了解到如何运用计算机程序对窗口进行剪裁,了解到编码剪裁算法直观方便,速度较快,中点分割剪裁算法不用进行乘除运算,剪裁效率高,Liang-Barsky直线裁剪算法更快。