Sutherland-Hodgman算法
Sutherland-Hodgman算法也叫逐边裁剪法,该算法是萨瑟兰德(I.E.Sutherland)和霍德曼(Hodgman)在1974年提出的。这种算法采用了分割处理、逐边裁剪的方法。
一,基本思想:
一次用窗口的一条边裁剪多边形。
考虑窗口的一条边以及延长线构成的裁剪线该线把平面分成两个部分:可见一侧;不可见一侧。多边形的各条边的两端点S、P。它们与裁剪线的位置关系只有四种
情况(1)仅输出1个顶点P;
情况(2)输出0个顶点;
情况(3)输出线段SP与裁剪线的1个交点I;
情况(4)输出线段SP与裁剪线的1个交点I和1个终点P
1、已知:多边形顶点数组src,顶点个数n,
定义新多边形顶点数组dest。
2、赋初值:用变量flag来标识:
0表示在内侧,1表示在外侧。
3、对多边形的n条边进行处理,对当前点号的考虑为:0~n-1。
for(i=0;i<n;i++)
{
if(当前第i个顶点是否在边界内侧?)
{
if(flag!=0) /*前一个点在外侧吗?*/
{
flag=0;/*从外到内的情况,将标志置0,作为下一次循环的前一点标志*/
(dest + j) =求出交点; /*将交点dest放入新多边形*/
j++;
}
(dest + j)= (src + i); /*将当前点srci放入新多边形*/
j++;
}
else
{
if(flag==0) /*前一个点在内侧吗?*/
{
flag=1;/*从内到外的情况,将标志置1,作为下一次循环的前一点标志*/
(dest + j) =求出交点; /*将交点dest放入新多边形*/
j++;
}
}
s= (src + i); /*将当前点作为下次循环的前一点*/
}
三,算法特点:
Sutherland-Hodgeman多边形裁剪算法具有一般性,被裁剪多边形可以是任意凸多边形或凹多边形,裁剪窗口不局限于矩形,可以是任意凸多边形。
上面的算法是多边形相对窗口的一条边界进行裁剪的实现,对于窗口的每一条边界依次调用该算法程序,并将前一次裁剪的结果多边形作为下一次裁剪时的被裁剪多边形,即可得到完整的多边形裁剪程序。
- //点在有向线段那侧
- /*
- 向量叉积法
- 为简单计,测试点表示为P点。假设窗口边界方向为顺时针,如图中所示,对于其中任一边界向量,从向量起点A向终点B看过去:
- 如果被测试点P在该边界线右边(即内侧),AB×AP的方向与X-Y平面垂直并指向屏幕里面,即右手坐标系中Z轴的负方向。
- 反过来,如果P在该边界线的左边(即外侧),这时AB×AP的方向与X-Y平面垂直并指向屏幕外面,即右手坐标系中Z轴的正方向。
- 设:点P(x,y)、点A(xA,yA)、点B(xB,yB),
- 向量AB={(xB-xA),(yB-yA)},
- 向量AP={(x-xA),(y-yA)},
- 那么AB×AP的方向可由下式的符号来确定:
- V=(xB-xA)·(y-yA)-(x-xA)·(yB-yA) (3-14)
- 因此,当V≤0时,P在边界线内侧;
- 而V>0时,P在边界线外侧。
- */
- static int _RtInSide(RtVector vector, RtPoint point)
- {
- return (vector.ep.x - vector.sp.x) * (point.y - vector.sp.y) - (vector.ep.y - vector.sp.y) * (point.x - vector.sp.x);
- }
- //多边形点必须是顺时针方向
- int rtPrunePSH(RtPoint* src, int num, RtPoint** dest, int* total)
- {
- int i = 0, j = 0, k = -1, flag = 0;
- RtPoint start, stop;//被剪裁多边形
- RtPoint sp, ep;//剪裁窗口
- RtPoint* temp = NULL;
- temp = (RtPoint*)malloc(sizeof(RtPoint) * 3 * (*total));
- if (temp == NULL) return -1;
- sp = *(src + num - 1);
- for ( i = 0; i < num; i++)//剪裁窗口
- {
- ep = *(src + i);
- start = *((*dest) + *total - 1);
- flag = _RtInSide(rtVector(sp, ep), start) > 0 ? 0 : 1;
- for ( j = 0; j < *total; j++)//被剪裁的多边形
- {
- stop = *((*dest) + j);
- if (_RtInSide(rtVector(sp, ep), stop) <= 0)//当前第i个顶点是否在边界内侧
- {
- if (flag == 0)/*前一个点在外侧吗?*/
- {
- flag = 1;/*从外到内的情况,将标志置0,作为下一次循环的前一点标志*/
- k++;
- CRtPoint<double> point;
- CRtPoint<int> st(sp.x, sp.y), et(ep.x, ep.y);
- CRtLine<int> v1(st, et);
- st.SetData(start.x, start.y);
- et.SetData(stop.x, stop.y);
- CRtLine<int> v2(st, et);
- v2.Intersect(v1,point);
- (temp + k)->x = point[0];/*将交点放入新多边形*/
- (temp + k)->y = point[1];
- }
- k++;
- *(temp + k) = stop;/*将当前点pi放入新多边形*/
- }
- else
- {
- if (0 != flag)/*前一个点在内侧吗?*/
- {
- flag = 0;/*从内到外的情况,将标志置1,作为下一次循环的前一点标志*/
- k++;
- CRtPoint<double> point;
- CRtPoint<int> st(sp.x, sp.y), et(ep.x, ep.y);
- CRtLine<int> v1(st, et);
- st.SetData(start.x, start.y);
- et.SetData(stop.x, stop.y);
- CRtLine<int> v2(st, et);
- v2.Intersect(v1,point);
- (temp + k)->x = point[0];/*将交点放入新多边形*/
- (temp + k)->y = point[1];
- }
- }
- start = stop;/*将当前点作为下次循环的前一点*/
- }
- sp = ep;
- *total = k + 1;
- memcpy(*dest, temp, sizeof(RtPoint) * (*total));
- k = -1;
- }
- return 0;
- }