[计算机图形学 with OpenGL] Chapter8 习题8.12 NLN二维线段裁剪算法实现

Posted 珍蜗蜗

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[计算机图形学 with OpenGL] Chapter8 习题8.12 NLN二维线段裁剪算法实现相关的知识,希望对你有一定的参考价值。

  Nicholl-Lee-Nicholl二维线段裁剪算法相对于Cohen-Sutherland和Liang-Barsky算法来说,在求交点之前进行了线段端点相对于几个区域的判断,可以确切的知道要求交点的边的信息。

  此方法只在二维空间裁剪时使用,C-S和L-B裁剪方法则可应用到三维空间。

 

算法步骤:

1   先使用C-S裁剪算法的区域码判断方法,去除一部分在裁剪区域外面的线段、显示在完全在裁剪区域内的线段。其他不能判断的情况,采用NLN算法进行裁剪。

2   p1和p2若有一点在区域内,必要时交换端点以确保p1在区域内。

   分别计算p1到裁剪区域四个顶点斜率m1-m4(从左下端点顺时针的4个顶点),判断线段斜率m与m1-m4的关系,决定计算哪条边与线段的交点。交点值赋给p2.

3   p1和p2有一个点在区域0001(左侧),必要时交换端点以确保p1在0001内。

   因为p2在区域内的情况被步骤2覆盖,因此p1为线段与左边界的交点。

   分别计算m1-m4,判断线段斜率m与m1-m4的关系,决定计算哪条边与线段的交点。此交点赋值给p2.

4   p1和p2有一个点在区域1001(左上角),必要时交换端点以确保p1在1001内。

   同样p2在区域内的情况被步骤2覆盖。

   分别计算m1-m4,判断线段斜率m与m1-m4的关系(注意此处需要判断m2与m4的大小,决定计算哪条边与线段的交点)。分别计算p1和p2。

5   对于其他6种情况,分别判断p1和p2的位置,在不同的区域,将线段裁剪区域旋转相应的角度,重复步骤2-4进行裁剪。裁剪完成后再将线段旋转回原来的位置。

 

  1 #include <GLUT/GLUT.h>
  2 #include <iostream>
  3 #include <math.h>
  4 #include "lineNLN.h"
  5 #include "linebres.h"
  6 
  7 GLfloat m1, m2, m3, m4;
  8 
  9 const GLint winLeftBitCode = 0x1; // 直接为1也没问题
 10 const GLint winRightBitCode = 0x2;
 11 const GLint winBottomBitCode = 0x4;
 12 const GLint winTopBitCode = 0x8;
 13 
 14 typedef GLfloat Matrix3x3 [3][3];
 15 Matrix3x3 matRotate;
 16 Matrix3x3 matComposite;
 17 const GLdouble pi = 3.14159;
 18 GLfloat delta;
 19 wcPt2D center;
 20 
 21 inline GLint inside (GLint code)
 22 {
 23     return GLint (!(code));
 24 }
 25 inline GLint reject (GLint code1, GLint code2)
 26 {
 27     return GLint (code1 & code2);
 28 }
 29 inline GLint accept (GLint code1, GLint code2)
 30 {
 31     return GLint (!(code1 | code2));
 32 }
 33 
 34 GLubyte encode (wcPt2D pt, wcPt2D winMin, wcPt2D winMax)
 35 {
 36     GLubyte code = 0x00;
 37     
 38     if(pt.getx() < winMin.getx())
 39         code = code | winLeftBitCode;
 40     if(pt.getx() > winMax.getx())
 41         code = code | winRightBitCode;
 42     if(pt.gety() < winMin.gety())
 43         code = code | winBottomBitCode;
 44     if(pt.gety() > winMax.gety())
 45         code = code | winTopBitCode;
 46     
 47     return code;
 48 }
 49 
 50 void swapPts (wcPt2D * p1, wcPt2D * p2) // TODO 为什么要用指针?
 51 {
 52     wcPt2D tmp;
 53     tmp = * p1; * p1 = * p2; * p2 = tmp;
 54 }
 55 
 56 void swapCode (GLubyte * c1, GLubyte * c2)
 57 {
 58     GLubyte tmp;
 59     tmp = * c1; * c1 = * c2; * c2 = tmp;
 60 }
 61 
 62 void renewWinVertexes (wcPt2D * winMin, wcPt2D * winMax)
 63 {
 64     wcPt2D tmp1, tmp2;
 65     tmp1.setCoords(fmin(winMin->getx(), winMax->getx()), fmin(winMin->gety(), winMax->gety()));
 66     tmp2.setCoords(fmax(winMin->getx(), winMax->getx()), fmax(winMin->gety(), winMax->gety()));
 67     * winMin = tmp1;
 68     * winMax = tmp2;
 69 }
 70 
 71 void matrix3x3SetIdentity (Matrix3x3 matIden3x3)
 72 {
 73     GLint row, col;
 74     for(row = 0; row < 3; row++)
 75     {
 76         for(col = 0; col < 3; col++)
 77         {
 78             matIden3x3[row][col] = (row == col);
 79         }
 80     }
 81 }
 82 
 83 void matrix3x3Premultiply (Matrix3x3 m1, Matrix3x3 m2)
 84 {
 85     GLint row, col;
 86     Matrix3x3 matTemp;
 87     
 88     for(row = 0; row < 3; row++)
 89     {
 90         for(col = 0; col < 3; col++)
 91         {
 92             matTemp[row][col] = m1[row][0] * m2 [0][col] + m1[row][1] * m2 [1][col] + m1[row][2] * m2 [2][col];
 93         }
 94     }
 95     
 96     for(row = 0; row < 3; row++)
 97     {
 98         for(col = 0; col < 3; col++)
 99         {
100             m2[row][col] = matTemp[row][col];
101         }
102     }
103 }
104 
105 void rotate2D (wcPt2D pivotPt, GLfloat theta)
106 {
107     matrix3x3SetIdentity(matRotate);
108     
109     matRotate[0][0] = cos(theta);
110     matRotate[0][1] = -sin(theta);
111     matRotate[0][2] = pivotPt.getx() * (1 - cos(theta)) + pivotPt.gety() * sin(theta);
112     matRotate[1][0] = sin(theta);
113     matRotate[1][1] = cos(theta);
114     matRotate[1][2] = pivotPt.gety() * (1 - cos(theta)) + pivotPt.getx() * sin(theta);
115 }
116 
117 void transformVerts2D (wcPt2D * verts)
118 {
119     GLfloat tempx, tempy;
120     
121     tempx = matRotate[0][0] * verts->getx() + matRotate[0][1] * verts->gety() + matRotate[0][2];
122     tempy = matRotate[1][0] * verts->getx() + matRotate[1][1] * verts->gety() + matRotate[1][2];
123     
124     verts->setCoords(tempx, tempy);
125 }
126 
127 void slopeWith4Vertexes (wcPt2D winMin, wcPt2D winMax, wcPt2D p1)
128 {
129     m1 = (p1.gety() - winMin.gety()) / (p1.getx() - winMin.getx());
130     m2 = (p1.gety() - winMax.gety()) / (p1.getx() - winMin.getx());
131     m3 = (p1.gety() - winMax.gety()) / (p1.getx() - winMax.getx());
132     m4 = (p1.gety() - winMin.gety()) / (p1.getx() - winMax.getx());
133     
134 //    std::cout << "slope : m1 : " << m1 << " m2 : " << m2 << " m3 : " << m3 << " m4 : " << m4 << std::endl;
135 }
136 
137 void lineClipNLN (wcPt2D winMin, wcPt2D winMax, wcPt2D p1, wcPt2D p2)
138 {
139     GLubyte code1, code2;
140     GLint plotLine = false, done = false;
141     GLfloat m = 0.0;
142     
143     while (!done)
144     {
145         code1 = encode(p1, winMin, winMax);
146         code2 = encode(p2, winMin, winMax);
147         if(accept(code1, code2))
148         {
149             plotLine = true;
150             done = true;
151         }
152         else
153         {
154             if(reject(code1, code2))
155             {
156                 std::cout << "1 rejected line!" << std::endl;
157                 done = true;
158             }
159             else
160             {
161                 // 有一个点在裁剪区域内
162                 if(inside(code1) || inside(code2))
163                 {
164                     plotLine = true;
165                     done = true;
166                     if(!inside(code1))
167                     {
168                         swapPts(&p1, &p2);
169                         swapCode(&code1, &code2);
170                     }
171                     wcPt2D topLeft, bottomRight;
172                     topLeft.setCoords(winMin.getx(), winMax.gety());
173                     bottomRight.setCoords(winMax.getx(), winMin.gety());
174                     if(p1.equals(winMin) || p1.equals(winMax) || p1.equals(topLeft) || p1.equals(bottomRight))
175                     {
176                         p2.setCoords(p1.getx(), p1.gety());
177                     }
178                     else
179                     {
180                         slopeWith4Vertexes(winMin, winMax, p1);
181                         
182                         if(p1.getx() != p2.getx())
183                         {
184                             m = (p2.gety() - p1.gety()) / (p2.getx() - p1.getx());
185                         
186                             if(m <= m1 && m >= m2 && (code2 & winLeftBitCode))
187                             {//L
188                                 p2.setCoords(winMin.getx(), p1.gety() + m * (winMin.getx() - p1.getx()));
189                             }
190                             else if(m <= m3 && m >= m4 && (code2 & winRightBitCode))
191                             {//R
192                                 p2.setCoords(winMax.getx(), p1.gety() + m * (winMax.getx() - p1.getx()));
193                             }
194                             else if((m > m3 || m < m2) && (code2 & winTopBitCode))
195                             {//T
196                                 p2.setCoords(p1.getx() + (winMax.gety() - p1.gety())/m, winMax.gety());
197                             }
198                             else if((m > m1 || m < m4) && (code2 & winBottomBitCode))
199                             {//B
200                                 p2.setCoords(p1.getx() + (winMin.gety() - p1.gety())/m, winMin.gety());
201                             }
202                         }
203                         else
204                         {
205                             if(p2.gety() > winMax.gety())
206                             {
207                                 p2.setCoords(p2.getx(), winMax.gety());
208                             }
209                             else
210                             {
211                                 p2.setCoords(p2.getx(), winMin.gety());
212                             }
213                         }
214                     }
215                 }
216                 else
217                 {
218                     if(code1 == 0x01 || code2 == 0x01)
219                     // 有一个点的区域码是0001,即在裁剪区域正左侧,L的情况在上一个if中包含,这里只会有LT,LR,LB和rejected的情况
220                     {
221                         if(code1 != 0x01)
222                         {
223                             swapPts(&p1, &p2);
224                             swapCode(&code1, &code2);
225                         }
226                         slopeWith4Vertexes(winMin, winMax, p1);
227                         if(p1.getx() != p2.getx())
228                         {
229                             m = (p2.gety() - p1.gety()) / (p2.getx() - p1.getx());
230                         }
231                         if(m < m1 || m > m2)
232                         {
233                             std::cout << "2 rejected line!" << std::endl;
234                             done = true;
235                         }
236                         else
237                         {
238                             p1.setCoords(winMin.getx(), p1.gety() + m * (winMin.getx() - p1.getx()));
239                             if((m <= m2 && m >= m3) && (code2 & winTopBitCode))
240                             {//LT
241                                 p2.setCoords(p1.getx() + (winMax.gety() - p1.gety())/m, winMax.gety());
242                             }
243                             else if((m < m3 && m >= m4) && (code2 & winRightBitCode))
244                             {//LR
245                                 p2.setCoords(winMax.getx(), p1.gety() + m * (winMax.getx() - p1.getx()));
246                             }
247                             else if((m < m4 && m >= m1) && (code2 & winBottomBitCode))
248                             {//LB
249                                 p2.setCoords(p1.getx() + (winMin.gety() - p1.gety())/m, winMin.gety());
250                             }
251                             plotLine = true;
252                             done = true;
253                         }
254                     }
255                     else if(code1 == (winLeftBitCode | winTopBitCode) || code2 == (winLeftBitCode | winTopBitCode))
256                         // 有一个点在裁剪区域外左上角的情况,这里的L、T情况已经在第一个if里包含
257                     {
258                         if(code1 != (winLeftBitCode | winTopBitCode))
259                         {
260                             swapPts(&p1, &p2);
261                             swapCode(&code1, &code2);
262                         }
263                         
264                         slopeWith4Vertexes(winMin, winMax, p1);
265                         if(p1.getx() != p2.getx())
266                         {
267                             m = (p2.gety() - p1.gety()) / (p2.getx() - p1.getx());
268                         }
269                         if(m > m3 || m < m1)
270                         {
271                             std::cout << "3 rejected line!" << std::endl;
272                             done = true;
273                         }
274                         else
275                         {
276                             plotLine = true;
277                             done = true;
278                             if(m <= m3 && m >= m4)
279                             {
280                                 p2.setCoords(winMax.getx(), p1.gety() + m * (winMax.getx() - p1.getx()));
281                                 if(m2 > m4 && m <= m2)
282                                 {//LR
283                                     p1.setCoords(winMin.getx(), p1.gety() + m * (winMin.getx() - p1.getx()));
284                                 }
285                                 else
286                                 {//TR
287                                     p1.setCoords(p1.getx() + (winMax.gety() - p1.gety())/m, winMax.gety());
288                                 }
289                             }
290                             else if(m < m4 && m >= m1)
291                             {
292                                 p2.setCoords(p1.getx() + (winMin.gety() - p1.gety())/m, winMin.gety());
293                                 if(m2 < m4 && m >= m2)
294                                 {//TB
295                                     p1.setCoords(p1.getx() + (winMax.gety() - p1.gety())/m, winMax.gety());
296                                 }
297                                 else
298                                 {//LB
299                                     p1.setCoords(winMin.getx(), p1.gety() + m * (winMin.getx() - p1.getx()));
300                                 }
301                             }
302                         }
303                     }
304                     else
305                     {
306                         center.setCoords((winMin.getx() + winMax.getx())/2, (winMin.gety() + winMax.gety())/2);
307                         
308                         if(code1 == (winLeftBitCode | winBottomBitCode) || code2 == (winLeftBitCode | winBottomBitCode))
309                         {//LB
310                             delta = -pi/2;
311                         }
312                         else if(code1 == winBottomBitCode || code2 == winBottomBitCode)
313                         {//B
314                             delta = -pi/2;
315                         }
316                         else if(code1 == (winBottomBitCode | winRightBitCode) || code2 == (winBottomBitCode | winRightBitCode))
317                         {//BR
318                             delta = pi;
319                         }
320                         else if(code1 == winRightBitCode || code2 == winRightBitCode)
321                         {//R
322                             delta = pi;
323                         }
324                         else if(code1 == (winRightBitCode | winTopBitCode) || code2 == (winRightBitCode | winTopBitCode))
325                         {//TR
326                             delta = pi/2;
327                         }
328                         else if(code1 == winTopBitCode || code2 == winTopBitCode)
329                         {//T
330                             delta = pi/2;
331                         }
332                         rotate2D(center, delta);
333                         transformVerts2D(&p1);
334                         transformVerts2D(&p2);
335                         transformVerts2D(&winMin);
336                         transformVerts2D(&winMax);
337                         renewWinVertexes(&winMin, &winMax);
338                     }
339                 }
340             }
341         }
342     }
343     
344     if(plotLine)
345     {
346         if(delta != 0)
347         {
348             rotate2D(center, -delta);
349             transformVerts2D(&p1);
350             transformVerts2D(&p2);
351         }
352         lineBres(round(p1.getx()), round(p1.gety()), round(p2.getx()), round(p2.gety()));
353     }
354     delta = 0;
355 }
NLN二维线段裁剪算法

 

 

完整代码路径:https://github.com/p0e0o0p0l0e0/Computer_Graphics/tree/f9a7ad1987586445d780de2461d423af0dd9ba6a

 

以上是关于[计算机图形学 with OpenGL] Chapter8 习题8.12 NLN二维线段裁剪算法实现的主要内容,如果未能解决你的问题,请参考以下文章

[计算机图形学 with OpenGL] Chapter8 习题8.12 NLN二维线段裁剪算法实现

[计算机图形学 with OpenGL] Chapter8 习题8.6 线段旋转后使用Cohen-Sutherland算法裁剪

计算机图形学(OPENGL):帧缓冲

计算机图形学(OPENGL):PBR理论

图形学计算机图形学知识点提纲2

计算机图形学(OPENGL):基础光照