码迷,mamicode.com
首页 > 编程语言 > 详细

Cohen Sutherland裁剪算法并使用OpenGL实践

时间:2016-08-15 22:01:48      阅读:175      评论:0      收藏:0      [点我收藏+]

标签:

  还是其几天写的,这是最简单的一个直线裁剪算法了吧,它针对的是一个矩形和一条线段。并且还是边与坐标轴平行的矩形。

  在实际应用上应该会经常用于屏幕对各种线段的裁剪吧。算法很简单效率也很高。

  首先是算法的两种特例:平凡接受和平凡拒绝。

技术分享(图片来自《计算机图形学(OpenGL)》第三版)

  当线段的两个端点都在矩形内部,则平凡接受,不需要裁剪。如图中的AB。而当线段的两个端点都在某条边的外边时,平凡拒绝,也不需要裁剪。如图中的CD。

  检测这两种情况的方法可以先形成两个端点的码字,如下:

技术分享

那么可以得到如下的几种码字:

技术分享

  如果两个点都在矩形内部,那么码字应该都是FFFF,则平凡接受,返回当前线段就好。而如果两个点的某一位都是T,则说明他们都在矩形某条边的外面,则平凡拒绝。如图3.17中的CD,对应的码字分别应是FTTF和FFTF,在第三位上都是T,他们都在矩形的右边,则平凡拒绝。

  我们运行这样的算法来裁剪一条线段:

技术分享

  

  最后,代码如下:

技术分享
  1 #include <GL/gl.h>
  2 #include <GL/glu.h>
  3 #include <GL/glut.h>
  4 #include <cmath>
  5 #include <iostream>
  6 using namespace std;
  7 
  8 struct Point2D
  9 {
 10     float _x, _y;
 11     Point2D()
 12     {
 13         _x = 0.0f;
 14         _y = 0.0f;
 15     }
 16     Point2D(const Point2D& p)
 17     {
 18         _x = p._x;
 19         _y = p._y;
 20     }
 21     Point2D(float xx, float yy)
 22     {
 23         _x = xx;
 24         _y = yy;
 25     }
 26     Point2D& operator=(const Point2D& p)
 27     {
 28         _x = p._x;
 29         _y = p._y;
 30         return *this;
 31     }
 32     Point2D& operator+(const Point2D& p)
 33     {
 34         Point2D temp;
 35         temp._x = _x + p._x;
 36         temp._y = _y + p._y;
 37         return temp;
 38     }
 39     Point2D& operator-(const Point2D& p)
 40     {
 41         Point2D temp(_x - p._x, _y - p._y);
 42         return temp;
 43     }
 44     float operator*(const Point2D& p)
 45     {
 46         return _x * p._x + _y * p._y;
 47     }
 48 
 49     float length()
 50     {
 51         return sqrtf(_x * _x + _y * _y);
 52     }
 53 };
 54 
 55 struct Line2D
 56 {
 57     Point2D _start;
 58     Point2D _end;
 59     float _length;
 60 
 61     Line2D() : _start(), _end()
 62     {
 63         _length = 0.0f;
 64     }
 65     Line2D(const Point2D& start, const Point2D& end) : _start(start), _end(end)
 66     {
 67     }
 68     Line2D(const Line2D& line) : _start(line._start), _end(line._end)
 69     {}
 70 
 71     float length()
 72     {
 73         _length = (_end - _start).length();
 74     }
 75 
 76     Line2D& operator = (const Line2D& line)
 77     {
 78         _start = line._start;
 79         _end = line._end;
 80     }
 81 };
 82 
 83 struct Rect
 84 {
 85     float _left;
 86     float _right;
 87     float _up;
 88     float _down;
 89 
 90     float width()
 91     {
 92         return _right - _left;
 93     }
 94     float height()
 95     {
 96         return _down - _up;
 97     }
 98 };
 99 
100 enum CutRes
101 {
102     CR_ACCEPTED = 0,
103     CR_REFUSED = 1,
104 };
105 
106 enum CSBIT
107 {
108     CB_BELOW = 0x01,//0001
109     CB_RIGHT = 0x02,//0010
110     CB_ABOVE = 0x04,//0100
111     CB_LEFT  = 0x08,//1000
112 
113     CB_BELOW_INV = 0xfe,//1111 1110
114     CB_RIGHT_INV = 0xfd,//1111 1101
115     CB_ABOVE_INV = 0xfb,//1111 1011
116     CB_LEFT_INV     = 0xf7,//1111 0111 
117 };
118 
119 typedef unsigned char KEY;
120 
121 /*Global Varibles*/
122 const int SCREEN_WIDTH = 800;
123 const int SCREEN_HEIGHT = 600;
124 Point2D g_Start;
125 Point2D g_End;
126 Line2D src;
127 Line2D dest;
128 bool acc;
129 Rect g_Rect;
130 int g_Count;
131 
132 KEY GenKey(const Point2D& p, const Rect& r)
133 {
134     KEY key = 0;
135 
136     if(p._y > r._down)
137     {
138         key |= CB_BELOW;
139     }
140     if(p._y < r._up)
141     {
142         key |= CB_ABOVE;
143     }
144     if(p._x < r._left)
145     {
146         key |= CB_LEFT;
147     }
148     if(p._x > r._right)
149     {
150         key |= CB_RIGHT;
151     }
152 
153     return key;
154 }
155 
156 void ShowKey(KEY key)
157 {
158     if(key & CB_LEFT)
159         cout << "T";
160     else
161         cout << "F";
162     
163     if(key & CB_ABOVE)
164         cout << "T";
165     else
166         cout << "F";
167     
168     if(key & CB_RIGHT)
169         cout << "T";
170     else
171         cout << "F";
172     
173     if(key & CB_BELOW)
174         cout << "T";
175     else
176         cout << "F";
177 }
178 
179 /*
180 key: TTFF
181 left above right below
182 */
183 int Cohen_Sutherland(const Line2D& src, Line2D& dest, const Rect& rect)
184 {
185     cout << "===============In Cohen_Sutherland===============\n";
186     Point2D start = src._start;
187     Point2D end = src._end;
188     KEY s, e;
189     dest = src;
190 
191     for(unsigned int i = 0; i < 4; ++i)
192     {
193         cout << "\nNow Line: start(" << start._x << ", " << start._y <<") end(" << end._x << ", " << end._y << ")\n";
194         
195         s = GenKey(start, rect);
196         e = GenKey(end, rect);
197         cout << "Key of Line: start ";ShowKey(s);cout << " end: ";ShowKey(e); cout << endl;
198 
199         if((s == e) && (s == 0))
200         {
201             //accept, all point inside the rect
202             dest._start = start;
203             dest._end = end;
204             return CR_ACCEPTED;
205         }
206         int _b = 1 << i;
207         if((s & _b) && (e & _b))
208         {
209             //all point at same side
210             return CR_REFUSED;
211         }
212 
213         switch(i)
214         {
215             case 0:
216             {
217                 //below
218                 if(s & _b)
219                 {
220                     float scale = (rect._down - end._y) / (start._y - end._y);
221                     start._x = (start._x - end._x) * scale + end._x;
222                     start._y = rect._down;
223                     cout << "Start Below Rect. Cutted: " << start._x << ", " << start._y << endl;
224                 }
225                 if(e & _b)
226                 {
227                     float scale = (rect._down - start._y) / (end._y - start._y);
228                     end._x = (end._x - start._x) * scale + start._x;
229                     end._y = rect._down;
230                     cout << "end Below Rect. Cutted: " << end._x << ", " << end._y << endl;
231                 }
232             }break;
233             case 1:
234             {
235                 //right
236                 if(s & _b)
237                 {
238                     float scale = (rect._right - end._x) / (start._x - end._x);
239                     start._x = rect._right;
240                     start._y = (start._y - end._y) * scale + end._y;
241                     cout << "start right Rect. Cutted: " << start._x << ", " << start._y << endl;
242                 }
243                 if(e & _b)
244                 {
245                     float scale = (rect._right - start._x) / (end._x - start._x);
246                     end._x = rect._right;
247                     end._y = (end._y - start._y) * scale + start._y;
248                     cout << "end right Rect. Cutted: " << end._x << ", " << end._y << endl;
249                 }
250             }break;
251             case 2:
252             {
253                 //above
254                 if(s & _b)
255                 {
256                     float scale = (rect._up - end._y) / (start._y - end._y);
257                     start._x = (start._x - end._x) * scale + end._x;
258                     start._y = rect._up;
259                     cout << "start above Rect. Cutted: " << start._x << ", " << start._y << endl;
260                 }
261                 if(e & _b)
262                 {
263                     float scale = (rect._up - start._y) / (end._y - start._y);
264                     end._x = (end._x - start._x) * scale + start._x;
265                     end._y = rect._up;
266                     cout << "end above Rect. Cutted: " << end._x << ", " << end._y << endl;
267                 }
268             }break;
269             case 3:
270             {
271                 //left
272                 if(s & _b)
273                 {
274                     float scale = (rect._left - end._x) / (start._x - end._x);
275                     start._x = rect._left;
276                     start._y = (start._y - end._y) * scale + end._y;
277                     cout << "start left Rect. Cutted: " << start._x << ", " << start._y << endl;
278                 }
279                 if(e & _b)
280                 {
281                     float scale = (rect._left - start._x) / (end._x - start._x);
282                     end._x = rect._left;
283                     end._y = (end._y - start._y) * scale + start._y;
284                     cout << "end left Rect. Cutted: " << end._x << ", " << end._y << endl;
285                 }
286             }break;
287         }
288     }
289     s = GenKey(start, rect);
290     e = GenKey(end, rect);
291 
292     cout << "At Last, Key of Line: start ";ShowKey(s);cout << " end: ";ShowKey(e); cout << endl;
293     if((s == e) && (s == 0))
294     {
295         //accept, all point inside the rect
296         dest._start = start;
297         dest._end = end;
298         return CR_ACCEPTED;
299     }
300     else
301     {
302         return CR_REFUSED;
303     }
304 }
305 
306 void myInit()
307 {
308     /*
309     Output Info
310     */
311 
312     g_Rect._up = 100;
313     g_Rect._down = 500;
314     g_Rect._left = 100;
315     g_Rect._right = 700;
316     g_Count = 0;
317     acc = false;
318     cout << "Rect: {" << g_Rect._left << ", " << g_Rect._up << ", " << g_Rect._right << ", "<< g_Rect._down << "}\n";
319 
320     glClearColor((float)0x66 / 0x100, (float)0xcc / 0x100, 1.0, 0.0);
321     glColor3f(0.0f, 0.0f, 0.0f);//Map Color Black
322     glPointSize(1.0);
323     glMatrixMode(GL_PROJECTION);
324     
325     glLoadIdentity();
326     gluOrtho2D(0.0, (GLdouble)SCREEN_WIDTH, (GLdouble)SCREEN_HEIGHT, 0.0);
327     glViewport(0.0, SCREEN_WIDTH, 0.0, SCREEN_HEIGHT);
328 }
329 
330 void myMouse(int button, int state, int x, int y)
331 {
332     if(button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)
333         return;
334 
335     cout << "MyMouse Called with " << x << ", " << y << endl;
336     switch(g_Count)
337     {
338     case 0:
339     {
340         ++g_Count;
341         g_Start._x = x;
342         g_Start._y = y;
343         src._start = g_Start;
344     }break;
345     case 1:
346     {
347         ++g_Count;
348         g_End._x = x;
349         g_End._y = y;
350         src._end = g_End;
351         acc = Cohen_Sutherland(src, dest, g_Rect);
352         if(acc)
353         {
354             cout << "Refused.\n";
355         }
356         else
357             cout << "Accept.\n";
358 
359         glutPostRedisplay();
360     }break;
361     case 2:
362     {
363         g_Start._x = x;
364         g_Start._y = y;
365         src._start = g_Start;
366         g_Count = 1;
367     }break;
368     }    
369 }
370 
371 void myDisplay()
372 {
373     glClear(GL_COLOR_BUFFER_BIT);
374 
375     //Draw Rect
376 
377     glColor3f(0.0f, 0.0f, 0.0f);//Rect
378     glPointSize(2.0);
379     glBegin(GL_LINE_STRIP);
380       glVertex2d(g_Rect._left, g_Rect._up);
381       glVertex2d(g_Rect._right, g_Rect._up);
382       glVertex2d(g_Rect._right, g_Rect._down);
383       glVertex2d(g_Rect._left, g_Rect._down);
384       glVertex2d(g_Rect._left, g_Rect._up);
385     glEnd();
386 
387     if(g_Count == 2)
388     {
389         //Draw Line
390         glColor3f(1.0f, 0.0f, 0.0f);//Normal Line, Red
391         glPointSize(2.0);
392         glBegin(GL_LINES);
393           glVertex2d(src._start._x, src._start._y);
394           glVertex2d(src._end._x, src._end._y);
395         cout << "\nDraw Line\n";
396         if(acc == CR_ACCEPTED)
397         {
398             //Draw Cutted Line
399             glColor3f(0.0f, 1.0f, 0.0f);//Normal Line, Green
400             glPointSize(2.0);
401               glVertex2d(dest._start._x, dest._start._y);
402               glVertex2d(dest._end._x, dest._end._y);
403             cout << "\nDraw CutLine\n";
404         }
405         glEnd();
406     }
407 
408     //glutSwapBuffers();
409     glFlush();
410     //cout << "Render Over\n";
411 }
412 
413 int main(int argc, char* argv[])
414 {
415     glutInit(&argc, argv);
416     //glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
417     glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
418     glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
419     glutInitWindowPosition(0, 0);
420     glutCreateWindow("Cohen Sutherland");
421     glutDisplayFunc(myDisplay);
422     glutMouseFunc(myMouse);
423   
424     myInit();
425     glutMainLoop();
426 
427     return 0;
428 }
Cohen_Sutherland_with_GL

 

  程序最上面是一些数据结构的定义,包括Point2D、Line2D、Rect等。

  然后码字我选择使用unsigned char类型的低四位,从高往低分别代表矩形的左、上、右、下。枚举类型CSBIT用来方便我进行位操作。

  算法核心函数是Cohen_Sutherland函数,接受一条直线和一个矩形为参数,一条直线作为输出并返回是否拒绝。GenKey函数和showKey分别用来生成码字和以友好的方式显示码字。

  其他则是OpenGL的东西了。程序运行后会有一个预先设定好的矩形,然后可以不断的通过鼠标点击选取起点和终点产生一条线段,并用之前的矩形进行裁剪。

  运行效果:

技术分享

Cohen Sutherland裁剪算法并使用OpenGL实践

标签:

原文地址:http://www.cnblogs.com/o--o/p/5774293.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!