还是其几天写的,这是最简单的一个直线裁剪算法了吧,它针对的是一个矩形和一条线段。并且还是边与坐标轴平行的矩形。
在实际应用上应该会经常用于屏幕对各种线段的裁剪吧。算法很简单效率也很高。
首先是算法的两种特例:平凡接受和平凡拒绝。
(图片来自《计算机图形学(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的东西了。程序运行后会有一个预先设定好的矩形,然后可以不断的通过鼠标点击选取起点和终点产生一条线段,并用之前的矩形进行裁剪。
运行效果:
时间: 2024-10-07 11:40:58