今天yogurt想要和大家分享一个大家在玩电脑时经常会用到的一个功能“窗口裁剪”的C语言编程实现方法~~相信用过QQ截屏或者其他截屏软件的盆友都知道截屏就是对一个图形或者图案用一个矩形框或者圆形框框起来,只保留框内的内容,而框外的内容自动舍去。那么它是怎么实现的呢?今天就让美丽可爱善良机智的yogurt来告诉你这个神奇的东东吧!

===================================yogurt小课堂开课啦===================================

    首先讲一下程序中用到的算法--Cohen-Sutherland端点编码算法。我们的每一个点在存储时除了要存储它的xy坐标,还要存储一个编码key,编码key由四个0/1的数字组成,0表示在窗口某边内,1表示在窗口某边外,如key=0101,表示该点在窗口左边界和下边界之外,如下图:

矩形窗口裁剪(以裁剪直线和复杂多边形为例)

    然后对待截取的直线,进行判断,若直线在窗口内(直线两端点的key都是0000)则简取,若直线两端点都在窗口外的同一侧(两个端点的key有同一位都是1)则简弃,对于其他情况(直线穿过窗口、直线未穿过窗口但是跨越三个界域)则进行较为复杂的判断,但是每次判断之前必须确保第一个点p1在窗口外,以便计算交点。得到交点之后用交点替代p1,继续重复进行待截取直线的判断,直到可以简取或者简弃为止。

    至于复杂多边形的裁剪,我们要考虑到由p1到p2是由外-->内,or由外-->外,or由内-->内,or由内-->外四种情况,每种情况对于点的处理是不同的。(以下是yogurt自己想到的算法,恩,就叫它“标记数判断法”吧O(∩_∩)O~)

    我们用a来进行特殊判断,当多边形某条边由内-->外时,记a为1。若是外-->外,就将a++,且判断若a==3,则证明该点的相邻两条边都在窗口外面(都是外-->外),则该点是舍去的且没有替代此点位置的点,将a重新置为2(代表这条线是外-->外,方便下一条线进行判断)。若是外-->内,判断若a==1,证明上一条边正好是从内-->外,则相对于被裁剪多边形来说,窗口内增加一个端点,然后把a置为0。如下图:

矩形窗口裁剪(以裁剪直线和复杂多边形为例)

====================================好,同学们,下课啦===================================

    接下来是Yogurt的个人炫技时间(*^__^*) 嘻嘻……

    首先我们看看简单的线段在窗口内的裁剪,上代码:

  1 // caijian.cpp : 定义控制台应用程序的入口点。
  2 //
  3 
  4 #include "stdafx.h"
  5 #include"Graph.h"
  6 
  7 typedef struct Key
  8 {
  9     int d3;
 10     int d2;
 11     int d1;
 12     int d0;
 13 }key;
 14 
 15 typedef struct Point
 16 {
 17     int x;
 18     int y;
 19     key c;
 20 }point;
 21 
 22 key code(point p, int xw1, int xwr, int ywb, int ywt);
 23 point asso(point p1, point p2,int xw1, int xwr, int ywb, int ywt);
 24 void drawline(point p1, point p2);
 25 
 26 int _tmain(int argc, _TCHAR* argv[])
 27 {
 28     point p1, p2;
 29     int xmin , xmax , ymin , ymax ;
 30 
 31     //printf("Please enter point one:");
 32     //scanf("%d,%d", &p1.x, &p1.y);
 33 
 34     //printf("Please enter point two:");
 35     //scanf("%d,%d", &p2.x, &p2.y);
 36     //
 37     //printf("Please enter the four boundary(xmin,xmax,ymin,ymax):");
 38     //scanf("%d,%d,%d,%d", &xmin, &xmax, &ymin, &ymax);
 39 
 40 
 41     /*测试数据*/
 42     p1.x = 3;
 43     p1.y = 10;
 44     p2.x = 100;
 45     p2.y = 120;
 46     xmin = 50;
 47     xmax = 150;
 48     ymin = 0;
 49     ymax = 180;
 50     
 51 
 52     setPenColor(RED);
 53     drawline(p1, p2);
 54 
 55     int x0 = int((xmin + xmax) / 2);
 56     int y0 = int((ymin + ymax) / 2);
 57     drawRectangle(x0,y0,xmax-xmin,ymax-ymin);
 58 
 59     p1.c = code(p1, xmin, xmax, ymin, ymax);
 60     p2.c = code(p2, xmin, xmax, ymin, ymax);
 61 
 62     while (1)
 63     {
 64         if (((p1.c.d0 | p2.c.d0 )== 0) && ((p1.c.d1 | p2.c.d1) == 0)&& ((p1.c.d2 | p2.c.d2) == 0 )&&( (p1.c.d3 | p2.c.d3) == 0)) //简取
 65             break;
 66         else if ((p1.c.d0 & p2.c.d0 == 1) || (p1.c.d1 & p2.c.d1 == 1 )|| (p1.c.d2 & p2.c.d2 == 1) || (p1.c.d3 & p2.c.d3 == 1 )) //简弃
 67             return 0;
 68         else
 69         {
 70             //确保p1在窗口外
 71             if ((p1.c.d0 == 0) && (p1.c.d1 == 0) && (p1.c.d2 == 0) && (p1.c.d3 == 0))  //若p1在窗口内
 72             {
 73                 point p3;
 74                 p3 = p1;
 75                 p1 = p2;
 76                 p2 = p3;
 77             }
 78         
 79             point s = asso(p1, p2, xmin, xmax, ymin, ymax);  //s为直线段p1p2与窗口的交点
 80             p1 = s;
 81         }
 82     }
 83     
 84     setPenColor(GREEN);
 85     drawline(p1, p2);
 86     return 0;
 87 }
 88 
 89 key code(point p, int xmin, int xmax, int ymin, int ymax)
 90 {
 91     if (p.x < xmin)
 92         p.c.d0 = 1;
 93     else
 94         p.c.d0 = 0;
 95 
 96     if (p.x>xmax)
 97         p.c.d1 = 1;
 98     else
 99         p.c.d1 = 0;
100 
101     if (p.y < ymin)
102         p.c.d2 = 1;
103     else
104         p.c.d2 = 0;
105 
106     if (p.y>ymax)
107         p.c.d3 = 1;
108     else
109         p.c.d3 = 0;
110 
111     return p.c;
112 }
113 
114 point asso(point p1, point p2, int xmin, int xmax, int ymin, int ymax)
115 {
116     double k = (p2.y - p1.y)*1.0 / (p2.x - p1.x);
117     
118     point s;
119 
120     if (p1.c.d0 == 1)//p1在左侧
121     {
122         s.x = xmin;
123         s.y = p1.y + k*(xmin - p1.x);
124 
125         if (s.y > ymax)//s应该在矩形上边界
126         {
127             s.y = ymax;
128             s.x = p1.x + (ymax - p1.y) / k;
129         }
130         else if (s.y < ymin)//s应该在矩形下边界
131         {
132             s.y = ymin;
133             s.x = p1.x + (ymin - p1.y) / k;
134         }
135     }
136     else  if (p1.c.d1 == 1)//p1在右侧
137     {
138         s.x = xmax;
139         s.y = p1.y + k*(xmax - p1.x);
140         
141         if (s.y > ymax)//s应该在矩形上边界
142         {
143             s.y = ymax;
144             s.x = p1.x + (ymax - p1.y) / k;
145         }
146         else if (s.y < ymin)//s应该在矩形下边界
147         {
148             s.y = ymin;
149             s.x = p1.x + (ymin - p1.y) / k;
150         }
151     }
152     else if (p1.c.d2 == 1)//p1在正下侧
153     {
154         s.y = ymin;
155         s.x = p1.x + (ymin - p1.y) / k;
156     }
157     else//p1在正上方
158     {
159         s.y = ymax;
160         s.x = p1.x + (ymax - p1.y) / k;
161     }
162     s.c = code(s, xmin, xmax, ymin, ymax);
163 
164     return s;
165 }
166 
167 void drawline(point p1, point p2)
168 {
169     moveTo(p1.x, p1.y);
170     lineTo(p2.x, p2.y);
171 }
View Code

相关文章: