在计算机图形学中,梁友栋—柏世奇算法是一个线段裁剪算法。梁友栋—柏世奇算法使用直线的参数方程和不等式组来描述线段和裁剪窗口的交集。求解出的交集将被用于获知线的哪些部分是应当绘制在屏幕上的,其基本思想是:在计算线段与裁剪窗交集之前做尽可能多的判断。
一条两端点为P1(x1,y1)、P2(x2,y2)的线段可以用参数方程形式表示:
式中,参数u在0~1之间取值,P(x,y)代表了该线段上的一个点,其值由参数u确定,由公式可知,当u=0时,该点为P1(x1,y1),当u=1时,该点为P2(x2,y2)。
如果点P(x,y)位于由坐标(xmin,ymin)和(xmax,ymax)所确定的窗口内,那么下式成立:
其可用4个不等式表达:
由此可计算最终线段:
与裁剪窗平行的直线在平行的边界上有;
若还满足,则线段全部在裁剪窗的外面,可以被消除;
当 时,线段从裁剪边界延长线的外部延伸到内部;
时,线段从裁剪边界延长线的内部延伸到外部;
对非零的, 计算出
;
对于每条直线,可以计算出参数 和
,该值定义了位于窗口内的线段部分:
的值由线段从外到内遇到的矩形边界所决定,对
检查
的边界,故令
为 max
;
的值由线段从内到外遇到的矩形边界所决定,对
检查
的边界,故令
为 min
;
若 ,则线段完全落在裁剪窗口之外,应当被舍弃;
示例代码如下:
void CCG5View::liang(CDC* pDC,double xw0, double yw0, double xw1, double yw1)
{
//计算dx与dy
double dx=xw1-xw0;
double dy=yw1-yw0;
//定义u与v
double u[4],v[4],t[4];
u[0]=-dx,u[1]=dx,u[2]=-dy,u[3]=dy;
v[0]=xw0-wxl,v[1]=wxr-xw0,v[2]=yw0-wyb,v[3]=wyt-yw0;
double tmax=0,tmin=1;
//线段与左右边界平行
if (u[0]==0&&u[1]==0)
{
if (v[0]<0 || v[1]<0)
return ;
for (int i=2;i<4;i++)
{
t[i]=v[i]/u[i];
if (u[i]<0)
tmax=max(t[i],tmax);
else
tmin=min(tmin,t[i]);
}
}
//线段与上下边界平行
else if (u[2]==0&&u[3]==0)
{
if (v[2]<0 || v[3]<0)
return ;
for (int i=0;i<2;i++)
{
t[i]=v[i]/u[i];
if (u[i]<0)
tmax=max(t[i],tmax);
else
tmin=min(tmin,t[i]);
}
}
else
{
for (int i=0;i<4;i++)
{
t[i]=v[i]/u[i];
if (u[i]<0)
tmax=max(t[i],tmax);
else
tmin=min(tmin,t[i]);
}
}
//直线在窗口外,舍弃之
if (tmax>tmin)
return ;
//进行窗视坐标转换
double fxw0=xw0+tmax*(xw1-xw0);
double fyw0=yw0+tmax*(yw1-yw0);
double fxw1=xw0+tmin*(xw1-xw0);
double fyw1=yw0+tmin*(yw1-yw0);
double xv0=a*fxw0+b;
double yv0=c*fyw0+d;
double xv1=a*fxw1+b;
double yv1=c*fyw1+d;
//绘制裁剪后的直线
pDC->MoveTo(ROUND(xv0),ROUND(yv0));
pDC->LineTo(ROUND(xv1),ROUND(yv1));
}
下面是算法的一个示例测试,编写一个放大镜小程序:
void CCG5View::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (magnify)
{
wxl=point.x-50.0/magtime;wxr=point.x+50.0/magtime;wyb=point.y-50.0/magtime;wyt=point.y+50.0/magtime;
vxl=point.x-50;vxr=point.x+50;vyb=point.y-50;vyt=point.y+50;
a=(vxr-vxl)/(wxr-wxl);b=vxl-wxl*a;
c=(vyt-vyb)/(wyt-wyb);d=vyb-wyb*c;
//使用双缓冲避免屏幕闪烁
liang_doublebuffer();
}
CView::OnMouseMove(nFlags, point);
}
//绘制六边形背景
void CCG5View::drawbk(CDC* pDC)
{
CPen pen(PS_SOLID,1,RGB(0,0,0));
pDC->SelectObject(&pen);
double r=200;
double beta=PI/3;
double alpha=0;
int n=15;
while (n--)
{
for (int i=0;i<6;i++)
{
p[n][i].x=int(r*cos(i*beta+alpha)+500+0.5);
p[n][i].y=int(r*sin(i*beta+alpha)+200+0.5);
}
pDC->Polygon(p[n],6);
alpha+=PI/90;
r-=10;
}
}
结果如下: