




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1本章內(nèi)容介紹視口和裁剪開發(fā)窗口到視口的變換開發(fā)一個經(jīng)典的裁剪算法創(chuàng)建世界坐標系中繪圖的工具開發(fā)選擇窗口和視口的方法,以達到最佳視覺效果基于正多邊形,弧線,圓建立圖形描述參數(shù)定義的曲線,并理解它們的繪制技術(shù)23.1概述屏幕坐標系:x方向從0到screenWidth-1,y方向從0到screenHeight-1,它是設(shè)備坐標系,有一個視口。世界坐標系:笛卡爾坐標系,邏輯坐標系。33.2世界窗口和視口信號處理中非常重要的函數(shù)sinc,它的定義為:當(dāng)x!=0,且sin(0)=1要繪制的曲線如右圖,比如限定x在-4到4變化,使用setWindow()來建立世界窗口,使用setViewport()來建立視口。通過設(shè)置一個世界窗口和一個視口,并建立它們之間的一個合適的映射,可以完成適當(dāng)?shù)目s放和平移。窗口是在世界坐標系,而視口是屏幕坐標系。繪制sinc的代碼見計算機圖形學(xué)課件2016\計算機圖形學(xué)(Opengl版第三版)書源代碼word版\第三章代碼.doc
3.2.1窗口到視口的映射世界窗口由它的左、上、右、下的邊界描述,分別是W.l,W.t,W.r,和W.b,視口在屏幕坐標系中,使用V.l,V.t,V.r,和V.b,單位是像素。世界窗口必須是對齊的矩形,但它可以有任意大小及出現(xiàn)在任何位置。視口也是矩形,位于屏幕窗口內(nèi)。給定窗口和視口的描述,可以得到一個映射或變換,即窗口到視口的映射。這個映射是一個等式,它對每一個在世界坐標下的點(x,y),產(chǎn)生屏幕坐標系中的一個點(sx,sy)。我們希望這個映射是保持比例的映射。例如x是位于窗口中距離左邊界40%處,則sx應(yīng)該位于視口中距離左邊界40%處。保持比例的性質(zhì)使得這個映射有線性形式:sx=A*x+Csy=B*y+D其中A,B,C和D是常數(shù)。常數(shù)A,B縮放x坐標和y坐標,而C和D平移它們。如何確定A,B,C和D呢?(sx-V.l)與視口寬度(V.r-V.l)的比例,必須等于(x-W.l)與窗口寬度(W.r-W.l)的比例。所以有:窗口到視口的變換見右邊。有如下性質(zhì):a.如果x在窗口的左邊界:x=W.l,則sx是在視口的左邊界sx=V.l;b.如果x是在窗口的右邊界,則sx是在視口的右邊界。c.對于某個比例f,如果x位于窗口寬度1/f處,則sx位于視口寬度的1/f處;d.如果x在窗口左邊界外(x<w.l),則sx在視口的左邊界外(sx<V.l)。此外,y到sy有類似的特征。例3.2.1考慮如右圖的窗口和視口。窗口的(W.l,W.r,W.b,W.t)=(0,2.0,0,1.0),視口的(V.l,V.r,V.b,V.t)=(40,400,60,300)。由右下角的等式得:A=180,C=40,B=240,D=60窗口到視口的映射為:sx=180x+40sy=240y+60練習(xí):建立映射對于世界窗口(10.0,10.0,-6.0,6.0)和視口(0,600,0,400),這個窗口和視口有同樣的縱橫比嗎?答:沒有因為長寬比一個為1.7,另一個為1.5.
設(shè)立窗口到視口的映射
OpenGL使得窗口到視口的變換很容易。OpenGL通過一系列變換完成所需要的映射,自動傳送到每個頂點,它還自動裁剪掉對象在世界窗口之外的部分。對于二維繪圖來說,世界窗口由函數(shù)gluOrtho2D()設(shè)定,它的原型是:voidgluOrtho2D(GLdoubleleft,GLdoubleright,GLdoublebottom,GLdoubletop);即左右下上
視口的設(shè)定通過glViewport()函數(shù),它的原型是:voidglViewport(GLintx,GLinty,GLintwidth,GLintheight);相當(dāng)于設(shè)置左下角(x,y)和右上角(x+width,y+height).即左下角和寬高。我們用setWindow()函數(shù)設(shè)置窗口,用setViewport()函數(shù)設(shè)置視口;//---------------setWindow---------------------即窗口的左右下上voidsetWindow(floatleft,floatright,floatbottom,floattop){glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(left,right,bottom,top);}//----------------setViewport------------------即視口的左右下上voidsetViewport(floatleft,floatright,floatbottom,floattop){glViewport(left,bottom,right–left,top-bottom);}例子3.2.2sinc函數(shù)回顧//---------------setWindow---------------------voidsetWindow(GLfloatleft,GLfloatright,GLfloatbottom,GLfloattop){glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D(left,right,bottom,top);}//----------------setViewport------------------voidsetViewport(GLintleft,GLintright,GLintbottom,GLinttop)//defineourownfunctiontospecifytheviewport,asexplainedlater{glViewport(left,bottom,right-left,top-bottom);}voidmyDisplay(void)//plotthesincfunction,usingworldcoordinates{ glClear(GL_COLOR_BUFFER_BIT);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glBegin(GL_LINE_STRIP); for(GLfloatx=-4.0;x<4.0;x+=0.1)//drawtheplotglVertex2f(x,sin(pi*x)/(pi*x));glEnd();glFlush();}在main函數(shù)中調(diào)用如下:setWindow(-5.0,5.0,-0.3,1.0);setViewport(0,640,0,480);例子3.2.3繪制文件中的折線因為dino.dat中的折線在一個矩形內(nèi),其兩個角分別是(0,0)和(640,480),故這個窗口沒有裁剪掉任何部分。例子3.2.4用恐龍圖形平鋪屏幕窗口setWindow(0,640.0,0,480.0);//左右下上for(inti=0;i<5;i++)//對于每一列{for(intj=0;j<5;j++)//對于每一行{glViewport(i*64,j*44,64,44);//左下角,寬高drawPolylineFile(“dino.dat”);//繪制恐龍}}詳細代碼見\計算機圖形學(xué)課件2016\計算機圖形學(xué)(Opengl版第三版)書源代碼word版\第三章代碼.doc例子3.2.4用恐龍圖形平鋪屏幕窗口 for(inti=0;i<5;i++) for(intj=0;j<5;j++) { if((i+j)%2==0) //if(i+j)是偶數(shù) setWindow(0.0,640.0,0.0,480.0);//正常的窗口 else setWindow(0.0,640.0,480.0,0.0);//顛倒的窗口 glViewport(i*64,j*44,64,44);//設(shè)置下一個視口 drawPolyLineFile("dino.dat"); //在窗口再繪制一遍}詳細代碼見\計算機圖形學(xué)課件2016\計算機圖形學(xué)(Opengl版第三版)書源代碼word版\第三章代碼.doc例子3.2.5裁剪圖片的某些部分 設(shè)置合適的窗口,可以裁剪一個圖片。OpenGL自動裁剪對象在世界窗口外面的部分。代碼見第三章.doc。voidmouseHandler(intbutton,intstate,intx,inty)函數(shù)中,點擊兩次鼠標,將鼠標坐標作為窗口的兩個角點,調(diào)用setWindow()函數(shù)設(shè)置窗口大小。相當(dāng)于對圖形的局部進行縮放。按下鍵r,重繪窗口。調(diào)用voidKBHandler(unsignedcharkey,intx,inty)函數(shù)??s放和平移:通過改變窗口,將窗口變小像用相機放大對象。將窗口放大類似于對象縮小。相機也可以漫游,順著場景滑行,在不同的時間拍攝不同的部分。通過將窗口平移到一個新的位置,漫游就可以容易實現(xiàn)。例3.2.6在動畫中放大圖片考慮建立一個動畫,這個動畫中相機逐步靠近右圖中六邊形的一部分。生成一系列的圖片,這些圖片通常被稱作幀,每一幀都使用較小的窗口。當(dāng)這些幀被快速播放時,這個視覺效果就像是相機在靠近對象。右圖所用到的幾個窗口,它們是同心的,并且具有固定的縱橫比,但相繼的幀的窗口尺寸在逐步縮小。對于其中的每一個窗口將視口內(nèi)所繪的圖案可視化。下面是偽代碼:floatcx=0.3,cy=0.2;//centerofthewindowfloatH,W=1.2,aspect=0.7;//windowpropertiessettheviewportfor(intframe=0;frame<NumFrames;frame++)//foreachframe{clearthescreen//erasethepreviousfigureW*=0.7;//reducethewindowwidthH=W*aspect;//maintainthesameaspectratiosetWindow(cx-W,cx+W,cy-H,cy+H);//setthenextwindowhexSwirl();//drawtheobject}實現(xiàn)一個連續(xù)的動畫:a.當(dāng)前的圖像穩(wěn)定顯示b.將當(dāng)前的圖像迅速用一個已繪制完成的新圖像替換。OpenGL使用雙緩存機制,glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);133.3裁剪線裁剪是圖形學(xué)的一個基本任務(wù),用于保持對象在給定區(qū)域外的部分不被繪出。在OpenGL的環(huán)境下,每個對象都會被一個特殊的算法自動裁剪到世界窗口。由于OpenGL為用戶進行了裁剪,但裁剪器所使用的想法,是非?;镜?。3.3.1如何裁剪一條線Cohen-Sutherland裁剪器計算端點為p1和p2的線段的哪一部分位于世界窗口內(nèi),并返回那個部分的端點。開發(fā)一個函數(shù):clipSegment(p1,p2,window),它接受兩個二維的點,以及一個對齊的矩形。3.3.1如何裁剪一條線裁剪行為為:1.如果整條線段都在窗口內(nèi)(如線段CD),函數(shù)返回1;2.如果整條線段在窗口為(如線段AB),函數(shù)返回0;3.如果一個端點在窗口內(nèi),另外一個在窗口外(如線段ED),函數(shù)將一個端點裁剪,并返回1;4.如果兩個端點都在窗口外,但有部分線段穿過窗口(如線段AE),函數(shù)將裁剪兩個端點,并返回1.對于一個窗口,有很多可能的線段排放方式。線段可以位于窗口的左邊,右邊,上面或者下面。還可以穿過窗口的任一個(或者兩個)邊界等等。通常的圖形有上千條線段,每條都必須依照窗口裁剪。Cohen-Sutherland提供一個快速的分治算法。3.3.2Cohen-Sutherland裁剪算法
Cohen-Sutherland算法通常在兩種情況下,可以快速的檢測和分發(fā)。這兩種情況被稱為平凡接受和平凡拒絕。如右圖,線段AB的兩個端點都在窗口W內(nèi),因此AB整個線段都在窗口內(nèi),故AB被平凡的接受,而不需要裁剪;而線段CD整個都在窗口W外,這時CD被平凡的拒絕,不會繪制。3.3.2Cohen-Sutherland裁剪算法
檢測平凡接受或平凡拒絕檢測線段是平凡接受或平凡拒絕是非??焖俚模驗樗恍枰獧z測端點的情況,而不是整條線段。需要一個快速算法來檢測一條線段是平凡的接受或者拒絕。我們給每個線段的每一個端點計算一個“窗口內(nèi)部/外部編碼”,用于后續(xù)的測試。一個點P與窗口邊界的相對位置有四種情況,左上右下。見右圖,P在窗口的左上,故左上為T,而右下為F,故P的編碼為TTFF;如果P在窗口里面,則P的編碼為TTTT;如果P在窗口的下面,不在窗口的左邊和右邊,則P的編碼為FFFT。右圖顯示了9種可能的區(qū)域,及其對應(yīng)的碼字。平凡接受:兩個碼字都是FFFF;平凡拒絕:兩個碼字在某一位元素上都是T:兩個點都在窗口左邊,則兩個碼字的第一位都是T;兩個碼字都在窗口上邊,則兩個碼字的第二位都是T;兩個點都在窗口右邊,則兩個碼字的第三位都是T;兩個碼字都在窗口下邊,則兩個碼字的第四位都是T;使用C/C++的為操作能力,可以有效地實現(xiàn)碼字的計算和檢測。FFFF3.3.2Cohen-Sutherland裁剪算法
沒有平凡接受或平凡拒絕時的截斷
Cohen-Sutherland算法使用一個分治的策略。如果線段不是被平凡接受和平凡拒絕,則它會被窗口的某一個邊界分成兩個部分。其中一個部分在窗口之外,這部分會被丟掉。另一部分有被看到的潛在可能性,因此整個過程將對四個窗口邊界的下一個重復(fù)進行,采用下面的策略:do{形成p1,p2的碼字;if(平凡接受)return1;if(平凡拒絕)return0;將線段在下一個窗口邊界處截斷;舍去在外面的部分;}while(1);這個算法最多四次循環(huán)就會終止。只保留線段在先前窗口邊界測試中幸存的部分,只有四個這樣的邊界。如右圖,點P1的坐標需要重新計算。它的x坐標是W.right,即窗口的右邊界,它的y坐標需要用圖中d的值調(diào)整到P1.y得到。e=p1.x-W.rightdelx=p2.x-p1.x;dely=p2.y-p1.y;故可確定d,就可以得到新的P1.y:p1.y+=(W.right-p1.x)*dely/delx
只有當(dāng)線段平行邊界線時,delx才為0,而此時線段位于窗口之外,故這種情況在沒有平凡接受或平凡拒絕時的截斷出現(xiàn)。
3.3.2Cohen-Sutherland裁剪算法
ClipSegment()函數(shù)集合了上面的想法。代碼為:intclipSegment(Point2&p1,Point2&p2,RealRectW){do{if(平凡接受)return1;//部分可見if(平凡拒絕)return0;//完全不可見if(p1在窗口外面){if(p1在窗口左邊)用左邊界截斷,更新p1;elseif(p1在窗口右邊)用右邊界截斷,更新p1elseif(p1在窗口下面)用下邊界截斷,更新p1;elseif(p1在窗口上面)用上邊界截斷,更新p1}else//p2在窗口外面{if(p2在窗口左邊)用左邊界截斷,更新p2;elseif(p2在窗口右邊)用右邊界截斷,更新p2elseif(p2在窗口下面)用下邊界截斷,更新p2elseif(p2在窗口上面)用上邊界截斷,更新p2}}while(1);}每次執(zhí)行do循環(huán)時,每一個端點的碼字會重新計算和測試。當(dāng)平凡接受和拒絕的檢測失敗時,算法會檢測p1是不是在窗口外,如果是,則將這個端點裁剪到窗口邊界。這個算法按照左、右、下、上的順序裁剪。見上圖:第一次裁剪將p1改到A;第二次將p2改為B;第三次發(fā)現(xiàn)p1還在窗口的下面,故將A改為C,最后將p2改為D。3.3.3開發(fā)Canvas類
在世界坐標系工作是有意義的,基本形體裁剪和映射從世界窗口到視窗口。但是這種轉(zhuǎn)換需要適當(dāng)?shù)毓芾怼S性S多交互要素(點,矩形,映射等),我們應(yīng)當(dāng)封裝它們并限制程序員訪問它們避免細小的錯誤。我們應(yīng)當(dāng)確保不同部分能適當(dāng)?shù)爻跏蓟J褂妙?。我們開發(fā)了一個Canvas類,它提供了一個方便的繪制畫布可以繪制線段,多邊形等。它提供了一個簡單的方法來創(chuàng)建需要的屏幕窗口,并創(chuàng)建一個世界窗口和視口,它確保世界窗口能較好地映射到視口。它也提供了moveTo()和LineTo()函數(shù),許多程序員找到了合適的代碼,如“龜形”圖形。有許多方式來定義Canvas類:我們使用OpenGL,利用所有的OpenGL方法。但在案例3.4我們描述了一個完全不同的實現(xiàn)方法,我們提供了很多的工具。尤其是實現(xiàn)了CohenSutherland裁剪器。3.3.4一些有用的支持類在Canvas和其他類中提供一些共有的數(shù)據(jù)類型是方便的。我們定義了5個類。提供了構(gòu)造函數(shù)和其他函數(shù)處理每一種類型的對象。一些類提供了繪制函數(shù)使得它容易繪制類的實例。一些方法在類聲明時就實現(xiàn),其他的方法在練習(xí)中要求實現(xiàn)。類Point2:一個實數(shù)坐標系的點類A3.3.4一些有用的支持類
類Point2:一個實數(shù)坐標系的點類。classPoint2{public:Point2(){x=y=0.0f;}//constructor1Point2(floatxx,floatyy){x=xx;y=yy;}//constructor2voidset(floatxx,floatyy){x=xx;y=yy;}floatgetX(){returnx;}floatgetY(){returny;}voiddraw(void){glBegin(GL_POINTS);//drawthispointglVertex2f((Glfloat)x,(Glfloat)y);glEnd();}private:
floatx,y;};3.3.4一些有用的支持類
類classIntRect:一個整數(shù)坐標系的矩形類。classIntRect{public:IntRect(){l=0;r=100;b=0;t=100;}//constructors左右下上IntRect(intleft,intright,intbottom,inttop){l=left;r=right;b=bottom;t=top;}voidset(intleft,intright,intbottom,inttop){l=left;r=right;b=bottom;t=top;}voiddraw(void);//drawthisrectangleusingOpenGLprivate:intl,r,b,t;};3.3.4一些有用的支持類
類RealRect:一個實數(shù)坐標系的矩形類。classRealRect{public:IntRect(){l=0;r=100;b=0;t=100;}//constructors左右下上IntRect(floatleft,floatright,floatbottom,floattop){l=left;r=right;b=bottom;t=top;}voidset(floatleft,floatright,floatbottom,floattop){l=left;r=right;b=bottom;t=top;}voiddraw(void);//drawthisrectangleusingOpenGLprivate:floatl,r,b,t;};3.3.4一些有用的支持類
類Canvas:包括位置,一個窗口,一個視口,窗口到視口的映射的畫布類。classCanvas{public:Canvas(intwidth,intheight,char*windowTitle);//constructorvoidsetWindow(floatl,floatr,floatb,floatt);voidsetViewport(intl,intr,intb,intt);IntRectgetViewport(void);//獲取視區(qū)數(shù)據(jù)RealRectgetWindow(void);//獲取窗口數(shù)據(jù)floatgetWindowAspectRatio(void);//獲取寬高比voidclearScreen();voidsetBackgroundColor(floatr,floatg,floatb);voidsetColor(floatr,floatg,floatb);voidlineTo(floatx,floaty);voidlineTo(Point2p);voidmoveTo(floatx,floaty);voidmoveTo(Point2p);//其它的成員函數(shù)private:Point2CP;//在世界窗口中的當(dāng)前位置IntRectviewport;//當(dāng)前的視口RealRectwindow;//當(dāng)前的窗口//其它的成員變量};3.3.4使用Canvas類
Canvascvs(640,480,“tryoutCanvas”);//創(chuàng)建全局的canvas對象voiddisplay(void){cvs.clearScreen();//clearscreencvs.setWindow(-10.0,10.0,-10.0,10.0);//左右下上cvs.setViewport(10,460,10,460);cvs.moveTo(0,-10.0);//drawalinecvs.lineTo(0,10.0);RealRectbox(-2.0,2.0,-1.0,1.0);//構(gòu)造一個盒子box.draw();//繪制盒子...}//<<<<<<<<<<<<<<<<<<<<<<main>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>voidmain(void){//thewindowhasalreadybeenopenedintheCanvasconstructorcvs.setBackgroundColor(1.0,1.0,1.0);//backgroundiswhitecvs.setColor(0.0,0.0,0.0);//setdrawingcolorglutDisplayFunc(display);glutMainLoop();}3.3.4實現(xiàn)Canvas類
//<<<<<<<<<<<<<<<<<<<<<Canvas構(gòu)造函數(shù)>>>>>>>>>>>>>>>>Canvas::Canvas(intwidth,intheight,char*windowTitle){char*argv[1];//dummyargumentlistforglutInit()chardummyString[8];argv[0]=dummyString;//hookupthepointerintargc=1;//tosatisfyglutInit()glutInit(&argc,argv);glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);glutInitWindowSize(width,height);glutInitWindowPosition(20,20);glutCreateWindow(windowTitle);//openthescreenwindowsetWindow(0,(float)width,0,(float)height);//defaultworldwindowsetViewport(0,width,0,height);//defaultviewportCP.set(0.0f,0.0f);//initializetheCPto(0,0)}voidCanvas::moveTo(floatx,floaty){CP.set(x,y);}//<<<<<<<<<<<<<<<<<<<<<<<lineTo>>>>>>>>>>>>>>>>>>>>>>>voidCanvas::lineTo(floatx,floaty){glBegin(GL_LINES);glVertex2f((GLfloat)CP.x,(GLfloat)CP.y);glVertex2f((GLfloat)x,(GLfloat)y);//drawthelineglEnd();CP.set(x,y);//updatetheCPglFlush();}3.3.4實現(xiàn)Canvas類
voidCanvas::setWindow(floatl,floatr,floatb,floatt){glMatrixMode(GL_PROJECTION);glLoadIdentity();gluOrtho2D((GLdouble)l,(GLdouble)r,(GLdouble)b,(GLdouble)t);window.set(l,r,b,t);}完成練習(xí):a).voidsetViewport(intl,intr,intb,intt);b).IntRectgetViewport(void);c).RealRectgetWindow(void);d).voidclearScreen(void);e).voidsetBackgroundColor(floatr,floatg,floatb);f).voidsetColor(floatr,floatg,floatb);g).voidlineTo(Point2p);h).voidmoveTo(Point2p);i).floatgetWindowAspectRatio(void)3.3.4使用Canvas仿真:Fibonacci數(shù)y(k)=y(k-1)+y(k-2)
序列1,1,2,3,5,8,13....繪制k與y的曲線圖,查看增長率。(對數(shù))p(k)=y(k)\y(k-1)繪制比率,黃金比率3.3.5相關(guān)的繪制3.5.1.開發(fā)moveRel()和lineRel(),偏移量(dx,dy)voidCanvas::moveRel(floatdx,floatdy){CP.set(CP.x+dx,CP.y+dy);}voidCanvas::lineRel(floatdx,floatdy){floatx=CP.x+dx,y=CP.y+dy;lineTo(x,y);CP.set(x,y);}例子3.5.1.一個箭頭標記,見右圖,偽碼為:moveTo(第一個數(shù)據(jù)點);drawMarker();//繪制一個標記for(每一個其余的點){lineTo(下一個點);//繪制下一個線段drawMarker();//在當(dāng)前位置CP繪制一個標記}3.3.5繪制箭頭標記從worldCP開始繪制,先繪制左邊,再下面,最后右邊,使用相對偏移量來繪制:voidarrow(floatf,floath,floatt,floatw){//assumesglobalCanvasobject:cvscvs.lineRel(-w-t/2,-f);//downtheleftsidecvs.lineRel(w,0);cvs.lineRel(0,-h);cvs.lineRel(t,0);//acrosscvs.lineRel(0,h);//backupcvs.lineRel(w,0);cvs.lineRel(-w-t/2,f);}3.3.5繪制蛇(龜)形圖CP:當(dāng)前位置CD:當(dāng)前方向,從x軸正向逆時針(CCW)測量的度數(shù)。在Canvas類中添加函數(shù)來控制龜形圖。CD作為類的一個私有成員數(shù)據(jù)。并添加如下方法:1)turnTo(floatangle).轉(zhuǎn)動龜形圖到給定的角度,實現(xiàn)為:voidCanvas::turnTo(floatangle){CD=angle;}2)turn(floatangle).轉(zhuǎn)動龜形圖為一個逆時針方向的角度:voidCanvas::turn(angle){CD+=angle;}使用一個負值產(chǎn)生一個正方向旋轉(zhuǎn)。注意一個旋轉(zhuǎn)是一個相對的方向變化:我們沒有指定一個方向,僅有一個方向上的改變。這個提供了很強的繪制復(fù)雜龜形圖的方法。3)forward(floatdist,intisVisible).移動龜形圖,從當(dāng)前位置CP沿著一條當(dāng)前方向CD前進距離dist到新位置并更新CP.如果isVisible非0繪制一條線,如果為0,則不繪制。voidCanvas::forward(floatdist,intisVisible){constfloatRadPerDeg=0.017453393;//每一度對應(yīng)的弧度數(shù)=3.14/180floatx=CP.x+dist*cos(RadPerDeg*CD);floaty=CP.y+dist*sin(RadPerDeg*CD);if(isVisible)lineTo(x,y);elsemoveTo(x,y);}例子3.3.5創(chuàng)建一個在一個鉤子的基礎(chǔ)圖案上的圖形.基礎(chǔ)圖案鉤子如右圖,代碼如下:forward(3*L,1);//Listhelengthoftheshortsidesturn(90);forward(L,1);turn(90);forward(L,1);turn(90);把這段代碼放到hook()函數(shù)中。例子3.3.6折線構(gòu)成的螺旋線螺旋線可以由龜形圖案產(chǎn)生。一個螺旋線是折線,每一個后繼的折線都比它的前繼的折線大或者小通過一個固定的大小,或者一個固定方向的角度。一段偽代碼為:for(一些迭代數(shù)){forward(length,1);//在當(dāng)前方向上繪制一條線turn(angle);/旋轉(zhuǎn)角度anglelength+=increment;//增加線段程度}每次繪制時,角度和程度都會增加,可以繪制如右圖的圖形。3.3.6練習(xí)3.3.6繪制蛇(龜)形圖3.3.7繪制熟知的logo3.3.7用字符串命令繪制蛇(龜)形圖Turtle
F調(diào)用forward(d,1);{d為距離}L調(diào)用turn(60);{左轉(zhuǎn)}R調(diào)用turn(-60).{右轉(zhuǎn)}編程實現(xiàn)下面的字符串命令FLFLFLFRFLFLFLFRFLFLFLFR.(見第9章產(chǎn)生分形程序)3.3.8繪制曲徑一個曲徑如右圖,通常由一個連續(xù)的曲徑組成。一個經(jīng)??吹角鷱饺绻畔ED花瓶,中國或來自不同國家的面磚。這里的基本圖案是為蛇形圖。3.3.8繪制曲徑一個曲徑如右圖,通常由一個連續(xù)的曲徑組成。一個經(jīng)??吹降那鷱饺绻畔ED花瓶,中國或來自不同國家的面磚。這里的基本圖案是為蛇形圖。
曲徑a)由基本圖案b)構(gòu)成。設(shè)置線寬。繪制雕刻圖案。3.3.9繪制其他類型的曲徑3.3.10繪制精心制作的曲徑3.3.11實現(xiàn)螺旋線編寫polyspiral(floatlength,floatangle,floatincr,intnum);繪制螺旋線由多條折線組成。每條線段增加長度和旋轉(zhuǎn)角度。3.3.12螺旋線是一個迭代函數(shù)系統(tǒng)(IFS)嗎?你能把一個螺旋線描述為第二章定義的迭代函數(shù)嗎?指定每一次迭代的龜形圖迭代函數(shù)。3.3,13Polyspiral()函數(shù)的遞歸形式。重寫polyspiral()用遞歸形式,這樣polyspiral()有dist參數(shù),調(diào)用時dist+inc。加入停止標準。323.4正多邊形、圓和圓弧3.4.1正多邊形圖形學(xué)中經(jīng)常繪制的一種形狀是有n條邊的正多邊形。一個多邊形是正的,如果它是簡單的,而且所有的邊相等,所有的內(nèi)角相等。我們稱有n條邊的正多邊形為正n邊形,常見的例子是正四邊形(正方形),正八邊形等等。如果正n邊形有很多條邊,則這個正n邊形看上去會像一個圓。事實上這是繪制圓的方法。正n邊形的n個頂點位于一個圓上,即所謂的正n邊形的外接圓,其位置容易被計算。
3.4.2正n邊形的變種基于正n邊形頂點,可以繪制有趣的變種。a.正7邊形b.一個星形c.一個7花環(huán)例子3.4.1花環(huán)和黃金5花環(huán)花環(huán)是每個頂點和其他頂點相連的正n邊形。繪制花環(huán)很容易:只要將每一個點和其他頂點相連。繪制花環(huán)的代碼見第三章代碼.doc:右圖為5花環(huán)和它的無窮嵌套--五邊形和五角星例子3.4.2基于兩個同心多邊形的圖形
右圖顯示了一些由兩個同心圓建造的圖像。外半徑是R,內(nèi)半徑是fR,f為某個因子。每個圖形都使用了一個正n邊形的變種,其半徑在內(nèi)徑和外徑之間交替。練習(xí):練習(xí)3.4.1星形和花環(huán)將正五邊形每隔一個點相連,就可以得到一個五角星。推廣到任意一個n是奇數(shù)的正n邊形,改寫moveTo和lineTo函數(shù)練習(xí)3.4.2繪制一個著名的標志圖右圖為一個標志,它包括了一個圖片的三個實例,相互旋轉(zhuǎn)了一定的角度。3.4.3繪制圓弧和圓許多藝術(shù)、建筑和科學(xué)中的圖形需要將圓弧按某種有意義的方式放置。一個圓弧可以方便的通過其中心c,外接圓的半徑R以及開始的角度a和它展開的角度b來表示。我們約定如果b是正的,則圓弧從a逆時針方向展開。繪制圓弧的代碼:voiddrawArc(Point2center,floatradius,floatstartAngle,floatsweep){//startAngleandsweepareindegreesconstintn=30;//numberofintermediatesegmentsinarcfloatangle=startAngle*3.14159265/180;//initialangleinradiansfloatangleInc=sweep*3.14159265/(180*n);//angleincrementfloatcx=center.getX(),cy=center.getY();cvs.moveTo(cx+radius*cos(angle),cy+radius*sin(angle));for(intk=1;k<n;k++,angle+=angleInc)cvs.lineTo(cx+radius*cos(angle),cy+radius*sin(angle));}可以寫一個程序drawCircle(),它通過圓心和半徑來繪制。還有其他方式來描述:1.已知圓心以及一個圓上的點。這里只需要半徑,就可以調(diào)用畫圓的函數(shù);2.給定圓必須經(jīng)過的三個點。因為三個不共線的點可以唯一確定一個圓。例子3.4.3將圓弧融合在一起使用兩個互相相切的圓的一部分,可以得到更復(fù)雜的形狀。由粗線所示的兩個圓弧在A點緊密地融合在一起,沒有視覺上的棱角和裂痕。練習(xí)3.4.4練習(xí)3.4.5繪制陰陽符號的代碼見第三章代碼.doc;3.4.4曲線的逐次細化
這一篇關(guān)于分形圖像,當(dāng)然只是入門。分形通常被定義為“一個粗糙或零碎的幾何形狀,可以分成數(shù)個部分,且每一部分都(至少近似地)是整體縮小后的形狀”,即具有自相似的性質(zhì)。分形有幾種類型,可以分別依據(jù)表現(xiàn)出的精確自相似性、半自相似性和統(tǒng)計自相似性來定義。雖然分形是一個數(shù)學(xué)構(gòu)造,它們同樣可以在自然界中被找到,這使得它們被劃入藝術(shù)作品的范疇。之前有做過一個鏤墊的程序,現(xiàn)在要做的是康托集,謝爾賓斯基地毯和Koch雪花。1)一維康托集:康托爾集是由不斷去掉線段的中間三分之一而得出。首先從區(qū)間[0,1]中去掉中間的三分之一(1/3,2/3),留下兩條線段:[0,1/3]∪[2/3,1]。然后,把這兩條線段的中間三分之一都去掉,留下四條線段:[0,1/9]∪[2/9,1/3]∪[2/3,7/9]∪[8/9,1]。把這個過程一直進行下去。divide_cantuo1中,首先將ab直線繪制出來,然后計算出1/3和2/3處的點坐標(y方向要移動0.5個單位),然后在分別繪制起點到1/3處和2/3處到終點的曲線。2)謝爾賓斯基地毯
謝爾賓斯基地毯的構(gòu)造與謝爾賓斯基三角形相似,區(qū)別僅在于謝爾賓斯基地毯是以正方形而非等邊三角形為基礎(chǔ)的。將一個實心正方形劃分為的9個小正方形,去掉中間的小正方形,再對余下的小正方形重復(fù)這一操作便能得到謝爾賓斯基地毯。這次divide_cantuo2函數(shù)需要找的是12個點,建議在紙上面畫一下。m=2和m=8的效果見下面中間兩個圖。cantuo2就是畫矩形。3)逐次細化一個簡單的曲線,可以遞歸成非常復(fù)雜的曲線。最簡單的例子是1904年瑞典數(shù)學(xué)家HelgevonKoch發(fā)現(xiàn)的Koch曲線。Koch雪花構(gòu)造過程:給定線段AB,科赫曲線可以由以下步驟生成:將線段分成三等份(AC,CD,DB)以CD為底,向外(內(nèi)外隨意)畫一個等邊三角形DMC將線段CD移去分別對AC,CM,MD,DB重復(fù)1~3??坪昭┗ㄊ且缘冗吶切稳吷傻目坪涨€組成的。每條科赫曲線的長度是無限大,它是連續(xù)而無處可微的曲線。分形代碼見第三章代碼.doc.363.5曲線的參數(shù)形式
描述曲線的形狀主要有兩種方法;隱式形式和參數(shù)形式,隱式形式用函數(shù)F(x,y)來描述曲線,并且提供x和y坐標的關(guān)系:當(dāng)且僅當(dāng)(x,y)滿足下式時,(x,y)在曲線上:F(x,y)=0(x,.y)在曲線上的條件例如,一條穿過點A和點B的直線,有隱式形式:F(x,y)=(y-Ay)(Bx-Ax)-(x-Ax)(By-Ay)一個圓心在原點·半徑是R的圓,有隱式形式:使用隱式形式的一個好處是,你可以非常容易的判斷一個點是否在曲線上:只要在有疑問的點上計算F(x,y)就行了。有一類曲線,曲線的內(nèi)部和外部是有意義的。在這種情況下,F(x,y)也被叫做內(nèi)部-外部函數(shù)。其意義是:
F(x,y)=0對所有在曲線上的(x,y)F(x,y)>0對所有在曲線外的(x,y)F(x,y)<0對所有在曲線內(nèi)的(x,y)
有一些曲線對x來說是單值的。如果g(x)是單值的,那對于每一個x,都只有一個函數(shù)值。事實上,只有單值的函數(shù)是“合法”的函數(shù)。對于這些曲線,函數(shù)的隱式形式可以寫成F(x,y)=y-g(x)。另外一些函數(shù)對f來說是單值的,因此存在函數(shù)h(),使得曲線上所有點滿足x=h(y)。還有一些函數(shù)對x和y來說都不是單值的:F(x,y)=0不能寫成y=g(x)或者x=h(y)的形式。例如圓,可以表示成:但這里有兩個函數(shù),而不是一個。其中一個函數(shù)用正號,另一個函數(shù)用負號。3.5.1曲線的參數(shù)形式曲線的參數(shù)形式在參數(shù)取不同值的時候,會產(chǎn)生出曲線上不同的點。參數(shù)形式可以用于更廣的一類曲線,因此推薦使用這種形式,特別是當(dāng)人們想繪制或者分析曲線的時候。參數(shù)形式使人想起一個點隨著時間而運動,我們可以把它轉(zhuǎn)換成一支筆繪制曲線的運動。粒子沿曲線運動的路徑由兩個函數(shù)x()和y()確定,如果是三維的情況,有三個函數(shù):x()、y()和z(),它們確定粒子在時刻t的位置。參數(shù)t通常被看作是時間,而曲線本身就是粒子隨著時間在某一個區(qū)間內(nèi)的變化所經(jīng)過的點。對于任一曲線,如果我們可以設(shè)計出合適的函數(shù)x()和y()或者x()、y()和z(),則他們可以簡明而準確的表示出這條曲線。關(guān)于設(shè)計曲線和曲面的全面分析見第l0章。
直線和橢圓前面所示的直線通過A、B這兩個點,我們選擇一個參數(shù)形式,在t=0時訪同A點,在t=1時訪問B點,從面得到:
因此,當(dāng)t在0~1之間變化時,P(t)=(x(t),y(t))會掃過直線上在A與B之同的所有點。另外一個典型的例子是橢圓,它是圓的一個輕微的變化。它可以通過參數(shù)形式描述:x(t)=Wcos(t)y(t)=Hsin(t),0≤t≤2π這里,W是橢圓的“半寬",H是橢圓的'半高”,橢圓的一些幾何性質(zhì)會在練習(xí)中探討。當(dāng)W和H相等時,橢圓就是一個半徑為W的圓。下圖顯示了一個橢圓,以及它的x(.)和y(.)。當(dāng)t從0到2π變化時,點P(t)=(x(t),(t))沿著橢圓移動一周,起點(也是終點)是(W,0)。這個圖片顯示了在不同時刻點的位置。從參數(shù)形式求隱式形式--“隱式化”怎么才能通過參數(shù)形式得到隱式形式?基本的步驟是聯(lián)合x(t)和y(t)兩個方程,再設(shè)法消去變量t。這提供了一個對任意t都成立的關(guān)系。進行這個步驟并不總是那么容易。沒有一個適用于所有參數(shù)形式的簡單的步驟。但對于這個橢圓,我們可以對x/W和y/H取平方,并使用眾所周知的關(guān)系
得到下面這個大家所熟悉的橢圓方程:
下面的練習(xí)將探討橢圓和其他一些經(jīng)典曲線的性質(zhì)。它們還指出了橢圓曲線的一些有用的性質(zhì)。以后我們會用到這些性質(zhì)。
練習(xí)
練習(xí)3.5.l橢圓的幾何特征橢圓是到兩個焦點的距離和為常數(shù)的點的集合。圖中的(c,0)是其中一個焦點,(-c,0)是另一個焦點,
練習(xí)3.5.2離心率橢圓的離心率,e=c/W,用來衡量橢圓和圓的差別。圓的離心率是0。舉個有趣的例子,太陽系中的行星都有近似圓的軌道。e從l/l43(金星)變化l/4(冥王星)。地球的軌道離心率是1/60。當(dāng)離心率接近于1,那么這個橢圓就會變成一條直線.在變成直線前,e會非常接近1。當(dāng)橢圓的e=0.99時.它的高寬比H/W是多少?
練習(xí)3.5.3其他的圓錐曲線橢圓是三種圓錐曲線中的一種如圖,圓錐曲線是用一個平面來切開一個圓錐時形成的。這些圓錐曲線如下:橢圓:平面沿著圓錐的等分半圓錐切開。拋物線:平面平行于圓錐的邊。雙曲線:平面切開圓錐的兩個等分半平面。拋物線和雙曲線都有有趣且有用的幾何特性。他們都有簡單的隱式形式和參數(shù)形式。拋物線:隱式形式為參數(shù)形式為:
雙曲線:隱式形式為參數(shù)形式為:
3.5.2繪制參數(shù)曲線
當(dāng)某個曲線有參數(shù)表達形式可以使用時,可以直接繪制出這條曲線。這是參數(shù)形式相對于隱式形式的主要優(yōu)勢。如下圖所示。假定一個曲線C有參數(shù)形式P(t)=(x(t),y(t)),其中t從0變化到T。我們只需用非常緊湊的間隔來采集P(t)的樣本,從而僅使用直線段來很好的近似曲線進行繪制。選擇一個時間的序列{ti),對每一個ti,可以計算出曲線上的位置Pi=P(ti)=(x(ti),y(ti))。如下圖所示,曲線P(t)可以由基于點列Pi的折線來近似。如果采樣間隔足夠小,肉眼就會自然地將這些線段融合在一起,從而看到的是一條連續(xù)的曲線。在曲線變化劇烈的間隔里,應(yīng)將采集點設(shè)得更密一些,而在曲線波動平滑的地方,采樣點分布的更稀疏一些。采樣間隔究竟要多接近,或者近似曲線的質(zhì)量需求,要取決于當(dāng)時的情況。如果只是對特殊的曲線,代碼通??梢院喕?。橢圓可以n個等間距的t的值繪制出來,代碼如下:
#defineTWOPI2*3.1415926glBegin(GL_LINES);for(doublet=0;t<TWOPI;t+=TWOPI/n)glVertex2f(W*cos(t),H*sin(t));glEnd();
出于繪圖的目的,參數(shù)形式繞過了隱式形式和顯式形式的所有困難。曲線是可以多值的,并且可以任意多次自交。豎直的線也不需要特殊處理:此時,x(t)在某個時間間隔中是常數(shù)。后面我們會看到在三維空間中也可以直接繪制曲線:使用了t的三個函數(shù),在t時刻曲線上的點的坐標是(x(t),y(t),z(t))。3.5.3極坐標形狀極坐標可以用來表示和繪制一些有趣的曲線。如下圖所示,曲線上的每一點被一個角度theta(從x軸的正方向開始測量)和一個徑向距離r來表示。如果r和θ都是t的函數(shù),那么當(dāng)t変化時,(r(t),θ(t))就會繪制出一條曲線。這個曲線也可以用笛卡爾坐標(x(t),y(t))來表示,其中:x(t)=r(t)cos(θ(t)),y(t)=r(t)sin(θ(t))但對于一大類優(yōu)美的曲線,可以采用簡化形式。在這些例子中,半徑r被直接表示為θ的函數(shù),而繪制曲線的參數(shù)就是θ本身。對于每一個點(r,θ),對成的笛卡爾坐標(x,y)滿足:_
x=f(θ)·cos(θ)y=f(θ)·sin(θ)當(dāng)用這種方式表達時,曲線可以被單個函數(shù)f()完整表示出來。用極坐標形式表示的曲線,可以容易的生成和繪制出來。參數(shù)是θ,它可以在該形狀相符合的間隔變化。最簡單的例子是半徑為K的圓:f(θ)=K。下圖顯示了一些用極坐標表示的很簡單的圖形。心臟線:f(θ)=K(1十cos(θ))。玫瑰曲線:f(θ)=Kcos(nθ),其中n指定玫瑰中的花瓣個數(shù).后面給出了兩個例子。阿基米德螺線;f(θ)=Aθ(圖見書P112)。參數(shù)A指出螺旋增長的速度(注意當(dāng)θ=2Π時,螺旋的半徑是A2Π)在每一個例子中,K給出了曲線整體的尺寸。因為心臟線是周期的,它可以通過讓θ從0到2Π之間變化來繪制。玫瑰曲線在n是整數(shù)時是周期的。而當(dāng)θ從0開始增長時,阿基米德螺旋線也會永遠保持的增長.這種螺旋線被廣泛的用于制造凸輪(就像汽車中的凸輪軸),將旋轉(zhuǎn)運動變?yōu)榫€性運動。
圓錐曲線(橢圓,拋物線和雙曲線),都具有下面這種極坐標形式:其中,e是圓錐曲線的離心率。e=1時,曲線是拋物線,當(dāng)0≤e<l時,是一個橢圓,e>1時,是一個雙曲線
對數(shù)螺旋線
對數(shù)螺旋線(或“等角螺旋旋線'')圖見書P113,也是一種很重要的形狀。這個曲線在一個常數(shù)角度α處切割所有徑向線,其中a=cot(α)。這是唯一一種在任意的尺度變換下具有相同形狀的螺旋線。將這種螺旋線進行任意放大后,再經(jīng)過適當(dāng)?shù)男D(zhuǎn),可以和原螺旋線重合.。因此,它也被稱為是自相似的。之后我們還會者看到它與Mandelbrot集合(見附錄4)的聯(lián)系。類似的,旋轉(zhuǎn)等角螺旋線,會讓人覺得它變大了或者變小了。這種保持形狀的特性似乎被一些動物所采用,例如鸚鵡螺這種軟體動物(見下圖)。這種動物生長時,它的殼會沿著一條対數(shù)螺旋線増長,從而保持同樣的形狀。做一個迷人的邊注:鸚鵡螺相鄰的兩個腔的體積比是黃金分割率!本章小結(jié)
在這一章里,我們開發(fā)了幾個工具,它們可以使應(yīng)用程序的程序員使用最方便的世界坐標系。思考并直接解決手頭的問題。對象通過高精度的實數(shù)坐標系來定義和建模,而不需要考慮對象將顯示在屏幕的哪個位量,以及生成的圖像有多大。這些考慮都推遲到隨后對窗口和視口的選擇上--手工的或自動的--它們決定了對象哪些部分被顯示,以及如何顯示在屏幕上。這種方法將建模的階段和視圖階段分開,允許程序員或者用戶在各階段集中解決相關(guān)問題,而不需為顯示設(shè)備的細節(jié)而分心。窗口的使用,使放大或者縮小場景,以及漫游場景的不同部分變得非常容易。從日常使用照相機的經(jīng)驗可以熟悉這些操作。視口的使用,使程序員能夠?qū)D像或者圖像集放在顯示器上的期望的位置,從面合成最終的圖像。我們討論了保證窗口和視口的縱橫比不變的技術(shù),從而避免圖像變形。我們還開發(fā)了一些附加的工具,用于創(chuàng)建正多邊形,圓弧和圓。我們還介紹了曲線的參數(shù)形式,這是一種非常自然的描述曲線的方法,它使得繪制曲線變得很容易,即使是那些多值的,相互交叉的,或者在某些地方是垂直的曲線案例分析
案例3.l學(xué)習(xí)通輯圖和混沌的模擬(難度:II)第2章結(jié)尾,我們討論了迭代函數(shù)系統(tǒng)(IFS)。另一種IFS提供了對混沌世界的有趣的觀察。它需要適當(dāng)?shù)卦O(shè)置窗口和視口,,通過重復(fù)應(yīng)用稱為通輯圖的函數(shù)f(.),可以生成一系列的數(shù)值。這個函數(shù)通過下面的方程描述一條拋物線:f(x)=4λx(1-x)(檢査它確實描述了一條拋物線),其中λ是一個0到l之間的常數(shù)。從一個在0~1之間的起始點x0開始,迭代應(yīng)用函數(shù)f(.),可以得到軌跡(回憶在第2章的定義):
也就是說x1=f(x0),x2=f(f(x0)),x3=f(f(f(x0))),...,以此類推。下面我們研究一下這個序列是如何得到的。通過函數(shù)在x上的值得到一個新的值y(一個垂直的移動從x軸到(x,f(x)),然后再把y的值當(dāng)作下一個x的值(一個水平的移動從(x,f(x))到(f(x),f(x))),重復(fù)這個過程,就可以得到軌跡。下圖顯示了當(dāng)x在0~1之間變化時,拋物線y=4λx(1-x)的形狀。其中λ=0.7。(在x取多少的時候,這個曲線達到它的最大值?)這里我們選擇起點x0=0.l(一個任意值),在x軸的這一點上,向拋物線方向畫一條垂線,它將終止在0.252(檢査這個終止點)。在圖像上,這就是繪制一條從(0.1,0)到(0.1,0.252)的線。為了找到曲線上的下一個點,我們從(0.l,0.252)繪制一條水平線到y(tǒng)=x這條線,它會在(52)處終止,因此x1=0.252。下面我們計算函數(shù)在x1=0.252上的值。如圖所示,這在視覺上這就是移動到y(tǒng)=x這條線上。接下來在新的值上計算f(.),就是向拋物線繪制一條垂直的線。像其他lFS一樣,一直重復(fù)這個過程。從上一個點(x(k-1),x(k)),繪制一條水平線到(x(k),x(k)),再繪制一條垂直線到(x(k),x(k+1))。這些數(shù)值很快地收斂到這個穩(wěn)定的“吸引點"上,這是一個滿足f(x)=x的固定點(當(dāng)λ=0.7時,這個數(shù)值是多少?)這個'吸引點"不依賴于起始點;這個序列總是很快的收斂到一個最終的數(shù)值。
案例分析
案例3.l學(xué)習(xí)通輯圖和混沌的模擬
如果λ是一個很小的值,這個動作就會更加簡單:只有一個“吸引點''在x=0處。但是當(dāng)λ增大時,就會發(fā)生一些奇怪的事情。下圖顯示了λ=0.85時的情況。這個序列的軌跡進人一個無限的循環(huán),而不會到一個最終值。這里有幾個吸引點,圖中展示了在這個有限循環(huán)中,位于每條垂線上的吸引點。當(dāng)λ增加到臨界值λ=0.892486418··時,這個過程変成了一個真正的混沌。下圖是λ=0.9的例子。對大多數(shù)起始點,軌跡都是周期的,周期間的軌跡數(shù)目變得非常的大。其他一些點產(chǎn)生非周期的運動,并且起始點很小的改動會導(dǎo)致非常不同的行為。大多數(shù)研究人員都認為,系統(tǒng)的微小改交(在這個例子中,將起始點做微小改變,或者將λ在0.85~0.9之間変化)應(yīng)當(dāng)導(dǎo)致行為上的微小改變,并且像這種簡單的系統(tǒng)不會表現(xiàn)出極其復(fù)雜的行為。寫一個程序,允許用戶研究邏輯圖的重復(fù)迭代行為。用戶設(shè)量一個合適的窗口和視口,使得可以清晰的看到整個邏輯圖.進一步的當(dāng)用戶結(jié)定x0和λ的值,程序可以繪制出這個系統(tǒng)產(chǎn)生的有限循環(huán)。案例分析
案例3.2在C/C++中實現(xiàn)Cohen-Sutherland算法
(難度:II)在3.3.2節(jié)中描述了Cohen-Sutherland算法的基本流程。這里,我們將討論并充實一些在C/C++中的實現(xiàn)細節(jié),研究這些語言提供的高效的,底層的位操作。如圖所示,我們首先要形成一個'內(nèi)部/外部”碼字,它說明P點與窗口的相對位置,一個8位二進制的code就夠了:其中4位二進制用于捕獲4個信息。依次測試P點與各個窗口邊界,如果它在邊界外,對應(yīng)的位被設(shè)置為1,用來表示真。圖3.50顯示了這是怎么做到的。code被初始化為0,然后每一獨立位用0R位操作來設(shè)置為合適的值。數(shù)字8、4、2和1是簡單的掩碼。例如,因為8的二進制是00001000,對一個數(shù)字和8進行OR位操作,會將這個數(shù)字從右數(shù)的第4位設(shè)置為1。在裁剪器中,兩個端點p1和p2(如圖)都和窗口進行了測試,且它們的碼字code1和code2已經(jīng)形成。下面我們需要測試“平凡接受"和“平凡拒絕”?!て椒步邮?兩個端點都在內(nèi)部,所以codel和code2部是0。在C/C++中,這可以用OR位操作來迅速判斷:當(dāng)(codel|cod2)是0的時候,就會發(fā)生平凡接受。
平凡拒絕:如果兩個端點都位于窗口的同一側(cè),就會發(fā)生平凡拒絕,都位于窗口左邊,上面,下面或右邊。這等價于它們的碼字中至少在一個位上都是l。例如code1是0l10且code2是0l00,則pl在窗口的上面和右邊,而p2在上面但并不在左邊或右邊。由于兩個點都在窗口上面,所以該線段沒有任何部分位于窗口內(nèi)n。因此,平凡拒絕可以用code1和code2的位操作AND來簡單的判斷:如果它們在某些位上都是l,則codel&code2在那一位也是1,因此(codel&code2)是非0的。
既沒有平凡接受,也沒有平凡拒絕,就截斷。另一個實現(xiàn)問題是如何對窗口外的部分做有效的截斷。假設(shè)我們知道碼字為code的點P在窗口外。我們可以通過測試code的每一位來知道P位于窗口的哪一邊,再通過等式(3.5)就可以完成截斷。圖3.51是一個截斷的過程,它尋找新的點(例如圖3.20中的A)來替換P。它通過用一個掩碼和code進行AND位操作,來確定P和窗口的相対位置。它還需要傳入事先計算好的dely和delx。寫一個Cohen-Sutherland算法的完整實現(xiàn),將這里描述的代碼和3.3.2節(jié)中的代碼整合在一起。通過繪制一個窗口以及一組隨機進擇的線段來測試算法,將在窗口內(nèi)的部分顯示為紅色,窗口外的部分顯示為黒色。ChopLine(Point2&P,unsignedcharcode){if(code&8){//totheLeftP.y+=(window.l-P.x)*dely/delx);P.x=window.l;}elseif(code&2){//totheRightP.y+=(window.r-P.x)*dely/
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 賣書快遞合同范本
- 廣州課題申報書怎么寫
- 雙方簽訂獨家合同范本
- 各種合同范本里
- 調(diào)查現(xiàn)狀課題申報書
- 幼兒校級課題申報書范文
- 創(chuàng)鑫供貨合同范本
- 名酒酒廠供貨合同范本
- 化妝 攝影 服務(wù)合同范本
- 教研課題申報書
- 精品紅四川大學(xué)信紙?zhí)ь^logo
- 建設(shè)項目職業(yè)病防護設(shè)施設(shè)計專篇編制導(dǎo)則
- C21甾體化合物 (2)
- 船舶安檢缺陷處理建議表籍國內(nèi)航行海船
- 輻照交聯(lián)電線電纜型號說明
- 雨污水管道溝槽開挖專項施工方案(改)
- 云南省作家協(xié)會入會申請表及說明
- 城軌道交通認知實習(xí)任務(wù)書及指導(dǎo)書
- 避免同業(yè)競爭承諾函
- 中國民主同盟入盟申請表(填寫樣表)
- 新西蘭交通規(guī)則非常適用精編版
評論
0/150
提交評論