400 likes | 530 Views
書名 Java 於資料結構與演算法之實習應用 書號 PG20098 原著 河西朝雄 著 譯者 周明憲 / 徐堯譯. 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟 Building A, 10F, 112, Shin-Tai-Wu Road Sec. 1, Shijr, Taipei, Taiwan. 電話/ 02-26962869 傳真/ 02-26962867 台中辦事處/台中市文心路一段 540 號 4 樓 -1 4-1F, 540, Wen Shing Road, Sec. 1, Taichung, Taiwan.
E N D
書名 Java於資料結構與演算法之實習應用 書號 PG20098 原著 河西朝雄著 譯者 周明憲/徐堯譯 台北總公司/台北縣汐止市新台五路一段112號10樓A棟 Building A, 10F, 112, Shin-Tai-Wu Road Sec. 1, Shijr, Taipei, Taiwan. 電話/02-26962869 傳真/02-26962867 台中辦事處/台中市文心路一段540號4樓-1 4-1F, 540, Wen Shing Road, Sec. 1, Taichung, Taiwan. 電話/04-23287870 傳真/04-23108168 博碩網址:http://www.drmaster.com.tw
第八章 學習重點 • 我們導入座標以便將點的位置顯示出來,各點的位置關係如在直線上,則能以1次方程式呈現,各點如位於圓、橢圓、雙曲線、拋物線等曲線上,則能以2次方程式顯示出來。如此一來2維圖形就能以某種方程式(y=f(x))表示出來,因此以運算的方式就能解開圖形問題的解。這種學科稱為解析幾何學,其創始者為迪斯卡提(Rene Descartes) • 若將圖形以方程式表示出來,並以電腦加以解析的話,則以轉換座標方式進行的圖形平行移動、旋轉、放大與縮小等作業就能輕易的做到。此外, 3維空間座標也因而能投影至2維平面。 • 但如要以解析方式將自然界存在的物體,比如複雜的海岸線表示出來還很困難。目前碎形幾何(Fractals)可以表示這種複雜的圖形。這是以遞迴方式將圖形表示出來的方法,又稱為遞迴繪圖(recursive graphics)。
8-0 Java的繪圖處理 1 座標Java的畫面座標如右圖所示,y軸座標是朝畫面的下方前進。 由於這與數學座標的標示方式不同,因而採用圖8.2所示的座標系統。邏輯座標中的y軸座標朝畫面的上方前進,Java將此邏輯座標上的(WX1,WY1)-(WX2,WY2)區域(視窗)對應至畫面上的(VX1,VY1)-(VX2,VY2)區域(景象埠:view port)。
1 座標 • 將視窗上的座標(x,y)描繪成景象埠的座標時,可用下列的計算式計算: 視窗的設定 window(x1,y1,x2,y2) 將邏輯座標上的(x1,y1)-(x2,y2)範圍設定成視窗。 景象埠的設定 view(x1,y1,x2,y2) 將景象座標上的(x1,y1)-(x2,y2)範圍設定成景象埠。 …x方向的倍率 …y方向的倍率
2 Turtle Graphics Library • 下面為畫直線的2個方法: (1) 設起點與終點 (2) 設長度與方向 • Java製作了move、turn等2個method,使第2個方法得以在Java上使用。這個方法也適合用來製作Turtle Graphics。 • 用第2個方法來畫直線時,先要以描繪的目前位置(LPX,LPY)與目前角Angle為基點。
指定長度的直線 move(1) 從目前位置(LPX,LPY)起朝目前角Angle的方向畫長度為l的直線。 至指定位置的直線 move(x,y) 從目前位置(LPX,LPY)起畫直線到(x,y)。 描繪(目前角)方向的設定 setangle(a) 將目前角設定成a[°]。 目前位置的指定 setpoint(x,y) 將目前位置移至(x,y)。 描繪(目前角)方向的旋轉 turn(a) 將目前角旋轉a[°]。旋轉後的目前角修補成在0~360°之內。
8-1 move與turn • 例題56 多角形描繪正三角形~正九角形 只要如圖將move(1);turn(120); 執行3次,就可畫出正三角形。 畫正n角形時的旋轉角度為360/n[°]。
……………… class Rei56Panel extends Panel { public void paint(Graphics g) { Turtle t=new Turtle(g); int n,j; for (n=3;n<=9;n++) { t.setpoint(150,100); t.setangle(0); for (j=1;j<=n;j++) { t.move(80); t.turn(360.0/n); } } } }
練習問題56 螺旋圖案 • 反覆執行move與turn,並同時慢慢的縮減直線的長度 • 將turn的角度設為a,逐漸縮減的長度設為step,然後反覆執行move與turn,直至直線的長度等於10。 • 改變step與a的值,就能畫出不同的圖形。
……… class Dr56Panel extends Panel { public void paint(Graphics g) { Turtle t=new Turtle(g); double leng=200, // 邊的初始值 step=1, // 邊的減少值 a=89; // 旋轉角 t.setpoint(100,100); t.setangle(0); while (leng>10) { t.move(leng); t.turn(a); leng=leng-step; } } } …………
8-2 2維座標轉換 • 某點(x0, y0)經過各種轉換後,其各點(x, y)可分別以下列方式求出。 • 對稱移動對著與y軸平行的直線x=a進行對稱移動,則 相反的,若對著與x軸平行的直線進行對稱移動,則
旋轉移動 將點(x0, y0)旋轉θ,則 平行移動 將點(x0, y0)往x軸及y軸方向分別平行移動m與n,則 放大、縮小 將點(x0, y0)的x軸方向放大k倍,y軸方向放大1倍,則
例題57 對稱移動 • mirror的flag若為1,則對著x=m進行對稱移動,若flag為0,則對著y=m進行對稱移動。 void mirror(int flag,double m,double[] dat) { // 對稱移動 int i; for (i=1;i<=2*dat[0];i=i+2) { // dat[0]為資料數量 if (flag==1) // y軸中心 dat[i]=2*m-dat[i]; if (flag==0) // x軸中心 dat[i+1]=2*m-dat[i+1]; } } void draw(double[] dat) { // 描繪圖形 int i; t.setpoint(dat[1],dat[2]); // 起點 for (i=3;i<=2*dat[0];i=i+2) // dat[0]為資料數量 t.moveto(dat[i],dat[i+1]); } public void paint(Graphics g) { t=new Turtle(g); double[] a={11,0,80,5,75,17,80,20,60,15,55,0,55, 0,20,10,40,20,40,10,20,0,20}; t.window(-160,-160,160,160); t.view(0,0,400,400); draw(a); mirror(1,0.0,a);draw(a); mirror(0,0.0,a);draw(a); mirror(1,0.0,a);draw(a); }
練習問題57 旋轉移動 void rotate(double deg,Point p) { // 旋轉轉換 double dx,dy,rd=3.14159/180; dx=p.x*Math.cos(deg*rd)-p.y*Math.sin(deg*rd); dy=p.x*Math.sin(deg*rd)+p.y*Math.cos(deg*rd); p.x=(int)dx;p.y=(int)dy; } public void paint(Graphics g) { t=new Turtle(g); int j,k,n=5; Point[] p={new Point(0,0),new Point(100,0), new Point(100,200),new Point(0,200), new Point(0,0)}; t.window(-200,-200,200,200); t.view(0,0,400,400); for (j=0;j<12;j++) { for (k=0;k<n;k++) { multi(.8,.8,p[k]); rotate(30,p[k]); if (k==0) t.setpoint(p[k].x,p[k].y); else t.moveto(p[k].x,p[k].y); } } } 設有長方形的4點座標,對此長方形進行旋轉轉換與放大/縮小轉換作業,並將結果顯示出來
8-3 幾何繪圖 • 例題58 對稱圖案以三角形的重心為中心,再將基本圖形旋轉,反覆製作同樣的圖案 • 美麗的幾何圖案很乍見之下極為複雜,但這些圖案實際上是由基本的圖形(直線、多角形等)反覆製作而成的,具有協調性與規律性。 • 以正三角形的重心為中心,將基本圖形旋轉2次,每次均旋轉120°。由此作業產生的圖案會被收斂至正三角形之內,而形成1個新的圖形。皆下來將這個三角形圖形的倒立圖橫向描繪,如此反覆操作。
public void paint(Graphics g) { Turtle t=new Turtle(g); final int N=9; // 資料數 final double rd=3.14159/180; // 弧度 double x[]={35,19,10,3,0,-3,-10,-19,-35}, y[]={-20,-20,-5,-5,0,-5,-5,-20,-20}; int a,b,j,k; double m,h,vy,vx,px,py; m=70;h=m*Math.sqrt(3.0)/2; // 正三角形的邊的長度、高度 t.window(-m/2,-h/3,m/2,h*2/3); b=1; for (vy=20;vy<=330;vy=vy+h) { a=1; for (vx=50;vx<=500;vx=vx+m/2) { t.view((int)vx,(int)vy,(int)(vx+m),(int)(vy+h)); // 景象 for (j=0;j<3;j++) { for (k=0;k<N;k++) { px=x[k]*Math.cos(120*j*rd)-y[k]*Math.sin(120*j*rd); py=x[k]*Math.sin(120*j*rd)+y[k]*Math.cos(120*j*rd); if (a*b==-1) py=-py+h/3; // 逆像修補 if (k==0) t.setpoint(px,py); else t.moveto(px,py); } } a=-a; } b=-b; } } 正三角形的重心為將三角形的高h劃分成2:1的點。而描繪的範圍則設為 因此在製作逆像時,不只要將y座標的符號反轉過來,而且還要進行h/3的修正。 此外還使用旗標a與b,以便正像與逆像能夠一再交互產生。
8-4 3維座標轉換 • 圖8.18中有1個直方體,我們即使以與z軸平行的平行光線投射至直方體的x-y平面上,也只會出現如圖8.19之(a)所示的長方形,看起來沒有立體的感覺。若將y軸旋轉β°,然後以平行光線照射至x-y平面上,則會出現如圖8.19的(b)所示的圖形,若將x軸旋轉α°,然後以平行光線照射至x-y平面上,則會出現如圖8.19的(c)所示的看起來有立體感覺的圖形。 • 將平行光線投射至x-y平面上的投影作業稱為軸測投影,執行下列2個基本作業後,就可製圖了。
(1) 將立體物以y軸、x軸、z軸為軸心旋轉 旋轉角的正方向設為面對各軸正方向的順時鐘方向,y軸、x軸、z軸旋轉的角度則分別設為α、β、γ。旋轉的順序若設為y軸→x軸→z軸,則點(x,y,z)會轉換成: 設y軸的旋轉為β 設x軸的旋轉為α 設z軸的旋轉為γ (2) 將在以上的旋轉中所得到的座標平行的投射在z=0平面(x-y平面)上。 這並不難做到,因為將上面的結果(x3,y3,z3)中的忽略掉,即表示將光線平行投射到z=0平面上。 因此(1)的計算式中的與也就可以省略,並能簡化成下列的計算式: 投射至x-y平面的點的座標
例題59 軸測投影 • 設有1幢房子的資料,將此房子以軸測投影顯示出來。房子的各點的資料存在如下所示的陣列內。 dat[0].f=-1:dat[0].x=80:dat[0].y=50:dat[0].z=100 dat[0].f= 1:dat[1].x= 0:dat[1].y=50:dat[1].z=100 α=45° β=-45° γ=0° α=20° β=-45° γ=0° α=45° β=-45° γ=0°
void rotate(double ax,double ay,double az, double x,double y,double z,Point p) { double x1,y1,z1,x2,y2; x1=x*Math.cos(ay)+z*Math.sin(ay); // x軸的旋轉 y1=y; z1=-x*Math.sin(ay)+z*Math.cos(ay); x2=x1; // y軸的旋轉 y2=y1*Math.cos(ax)-z1*Math.sin(ax); p.x=(int)(x2*Math.cos(az)-y2*Math.sin(az)); // z軸的旋轉 p.y=(int)(x2*Math.sin(az)+y2*Math.cos(az)); }
練習問題59 透視 • 軸測投影是對著投影面投射平行光線,而透視則如圖8.20所示,對著某點投射集中的光線。這點稱為投影中心(消失點),為了使透視轉換能夠更容易操作,我們將此點設在z軸上。 • 在透視中,與投影中心相對的立體位置如有不同,則立體的感覺就不同,立體物若位於投影中心的上方,則透視的結果如同俯視立體物,反之,則透視的結果如同仰視立體物。 • 因此在透視中,除了將立體物旋轉的動作外,還加上了平行移動的操作。一般而言,看透視圖時,為了讓物體看起來很立體,只要旋轉y軸即可,不須旋轉x、y、z等3軸。
以下的計算式為透視的轉換計算式,為了簡化起見,旋轉時僅將y軸旋轉β?,並將x、y、z方向的平行移動量設為l、m、n,將投影中心社為z=-vp。轉換後的點則設為透視成z=0平面(x-y平面)的點。以下的計算式為透視的轉換計算式,為了簡化起見,旋轉時僅將y軸旋轉β?,並將x、y、z方向的平行移動量設為l、m、n,將投影中心社為z=-vp。轉換後的點則設為透視成z=0平面(x-y平面)的點。 先進行旋轉與平行移動作業,則 再將其透視成z=0平面,則 這種透視為2點透視,為最常用的方法。當β=0時,則為單點透視(圖8.21)
double ay=-35*3.14159/180, // y軸的旋轉角度 vp=-300.0, // 投影中心 l=-25.0, // x方向的移動量 m=-70.0, // y方向的移動量 n=0.0, // z方向的移動量 h,px,py; int k; t.window(-150,-150,150,150); t.view(0,0,400,400); for (k=0;a[k].f!=-999;k++) { // 透視轉換 h=-a[k].x*Math.sin(ay)/vp+a[k].z*Math.cos(ay)/vp+n/vp+1; px=(a[k].x*Math.cos(ay)+a[k].z*Math.sin(ay)+1)/h; py=(a[k].y+m)/h; if (a[k].f==-1) t.setpoint(px,py); else t.moveto(px,py); }
8-5 立體模型 立體圖形中比較容易製作的圖形有:錐狀體、柱狀體與旋轉體。 產生錐狀體所需的資料為底面各點的座標(x1,y1)、(x2,y2)、...(xn,xn)與投射至頂點的x-z平面的投影點(xc,xc)與高h。 產生柱狀體所需的資料為底面各點的座標(x1,y1)、(x2,y2)、...(xn,xn)與高h。 如要產生旋轉體,可將圖中a~h所示的2維圖形中的y軸旋轉即可。 若已知道各點的y座標與y軸的距離(半徑)r,則各點繞著y軸旋轉θ?時的座標如下: 我們是可以將這點旋轉轉換(以軸測投影所示的計算式進行),但光描繪a~h的旋轉軌跡,僅能單純的畫出8個橢圓,根本看不出有立體的感覺。因此我們將 連接a→b→c...h的直線(稜線)按某個旋轉角度分別畫在數個位置上。
class Rei60Panel extends Panel { void rotate(double ax,double ay,double az, double x,double y,double z,Point p) { double x1,y1,z1,x2,y2; x1=x*Math.cos(ay)+z*Math.sin(ay); // y軸的旋轉 y1=y; z1=-x*Math.sin(ay)+z*Math.cos(ay); x2=x1; // x軸的旋轉 y2=y1*Math.cos(ax)-z1*Math.sin(ax); p.x=(int)(x2*Math.cos(az)-y2*Math.sin(az)); // z軸的旋轉 p.y=(int)(x2*Math.sin(az)+y2*Math.cos(az)); } public void paint(Graphics g) { Turtle t=new Turtle(g); int n,k; final double rd=3.14159/180; double x,z,px,py,ax,ay,az; double[] y={180,140,100,60,20,10,4,0,-999}, // 高度 r={100,55,10,10,10,50,80,80,-999}; // 半徑 Point p=new Point(0,0); ax=35*rd; ay=0*rd; az=20*rd; t.window(-250,-250,250,250); t.view(0,0,400,400);
for (k=0;(int)y[k]!=-999;k++) { // y軸的旋轉軌跡 for (n=0;n<=360.0;n=n+10) { x=r[k]*Math.cos(n*rd); z=r[k]*Math.sin(n*rd); rotate(ax,ay,az,x,y[k],z,p); if (n==0) t.setpoint(p.x,p.y); else t.moveto(p.x,p.y); } } for (n=0;n<=360;n=n+60) { // 稜線 for (k=0;(int)y[k]!=-999;k++) { x=r[k]*Math.cos(n*rd); z=r[k]*Math.sin(n*rd); rotate(ax,ay,az,x,y[k],z,p); if (k==0) t.setpoint(p.x,p.y); else t.moveto(p.x,p.y); } }
8-6 陰線處理 前面已說明過將房子的資料以3維方式顯示的方法,而使用這種方法需要各頂點的資料,如要顯示複雜的立體物也需要大量的資料。 接下來我們將這種3維函數列出如下: 由於這項函數是以計算式的形式顯示,因此不需要用到資料,不用花太多心力就能畫出複雜的圖案。
public void paint(Graphics g) { Turtle t=new Turtle(g); final double rd=3.1415927/180; double x,y,z,px,py,ax,ay; ax=30*rd; ay=-30*rd; t.window(-300,-200,300,200); t.view(0,0,600,400); for (z=-200;z<=200;z=z+10) { for (x=-200;x<=200;x=x+5) { y=30*(Math.cos(Math.sqrt(x*x+z*z)*rd) // 3維函數 +Math.cos(3*Math.sqrt(x*x+z*z)*rd)); px=x*Math.cos(ay)+z*Math.sin(ay); // 旋轉轉換 py=y*Math.cos(ax)-(-x*Math.sin(ay) +z*Math.cos(ay))*Math.sin(ax); if ((int)x==-200) t.setpoint(px,py); else t.moveto(px,py); } }
練習問題61 max‧min法 • 陰線處理是指將隱藏在面的後方之見不到的線消除之作業。 • 陰線處理的方法有很多種,本書使用非常簡單的max‧min法。 • 如圖8.29所示,以max‧min法描繪圖形時,一定要從面向自己的一方開始畫起,而且這之後所描繪的點若位於之前所描繪的點群(位於與螢幕畫面同一x座標前的點群)的最大點與最小點之間(點群的內側),則不顯示該點,反之若位於這些點群之外,則顯示該點。
public void paint(Graphics g) { Turtle t=new Turtle(g); int[] ymin=new int[600],ymax=new int[600]; int k,px,py; final double rd=3.1415927/180; double x,y,z,ax,ay; ax=30*rd; ay=-30*rd; t.window(0,0,600,500); t.view(0,0,600,500); for (k=0;k<600;k++) { // 判定最大點與最小點的函數 ymin[k]=600;ymax[k]=0; } for (z=200;z>=-200;z=z-10){ for (x=-200;x<=200;x++){ y=30*(Math.cos(Math.sqrt(x*x+z*z)*rd) // 3維函數 +Math.cos(3*Math.sqrt(x*x+z*z)*rd)); px=(int)(x*Math.cos(ay)+z*Math.sin(ay)+300); // 旋轉轉換 py=(int)(y*Math.cos(ax)-(-x*Math.sin(ay) +z*Math.cos(ay))*Math.sin(ax)+250); if (py<ymin[px]) { // 比目前的最小點還小 ymin[px]=py;t.pset(px,py); } if (py>ymax[px]) { // 比目前的最大點還大 ymax[px]=py;t.pset(px,py); } }
8-7 遞迴繪圖Ⅰ • 單純的曲線(n次函數)能在座標系統中以解析的方式表示出來,也就是說若使用f(x)函數可將某一曲線表示出來的話,在某點x=x0中的f(x)就能以f(x0)求出來。 • 不過,曲線的型態越複雜就越不易以1個函數來表示整個曲面。因此就有近似方法的產生,這種方法將整個曲面分割成數塊小區域,再以解析的方式求出各小區域的f(x),其種類有spline曲線、最小平方近似與線段(segment)法。 • 將茂盛的樹木、複雜的出海口等自然界的物體以解析方式表示出來,是一件很麻煩(應該說近乎不可能)的事。 • 因此不以解析方式將圖案表示出來,而改以遞迴方式將圖案表現出來,這種繪圖方式稱為遞迴繪圖。這種繪圖方法引人入勝的地方在於,其可輕易的表示出接近自然的圖案,也令人產生科學能夠闡明自然與生命的神秘美之錯覺(也許不久以後就不是錯覺)。 • 自然界的生物在最初成行的階段都是1個細胞,然後經過無數次細胞的反覆分裂而成長,再形成現在的模樣。這種過程與遞迴顯示相當類似。換句話說,這種過程可以說是因為「n次的細胞(現在的模樣)是由n-1次的細胞所形成,n-1次的細胞則是由n-2次的細胞所形成......」的緣故。隨著0次細胞的模樣與成長的規則(+突變)的定義之不同,即可得到各種不同的物種。
描繪科赫曲線 • 科赫曲線是由數學家科赫(H.von Koch)所發現的,如圖8.30所示,0次的科赫曲線為長度為l的直線。1次的科赫曲線為1邊的長為l/3大的正三角形狀的角。2次的科赫曲線相對於1次科赫曲線的各邊(4個),為l/9大的正三角形狀。若無限的反覆操作下去,就成為由無數條的無限小長度的線條所組成的曲線。 • 描繪n次科赫曲線的演算法之大致內容為,如要描繪n次科赫曲線,則要按下面方式描繪4個n-1次的科赫曲線。這裡將科赫曲線的1邊的長度一直固定為leng。 (1)描繪1個n-1次的科赫曲線 (2)將角度改成60度,描繪1個n-1次的科赫曲線 (3)將角度改成-120度,描繪1個n-1次的科赫曲線 (4)將角度改成60度,描繪1個n-1次的科赫曲線
class Rei62Panel extends Panel { private Turtle t; void koch(int n,double leng) { // 科赫遞迴的動作程序 if (n==0) { t.move(leng); } else { koch(n-1,leng); t.turn(60); koch(n-1,leng); t.turn(-120); koch(n-1,leng); t.turn(60); koch(n-1,leng); } } public void paint(Graphics g) { t=new Turtle(g); int n=4; // 科赫曲線的次數 double leng=4.0; // 0次的長度 t.setpoint(50,200); t.setangle(0); koch(n,leng); } }
練習問題62-1 科赫島 void koch(int n,double leng) { // 科赫遞迴的動作程序 if (n==0) { t.move(leng); } else { koch(n-1,leng); t.turn(60); koch(n-1,leng); t.turn(-120); koch(n-1,leng); t.turn(60); koch(n-1,leng); } } public void paint(Graphics g) { t=new Turtle(g); int i,n=4; // 科赫次數 double leng=4.0; // 0次的長度 t.setpoint(30,300); t.setangle(0); for (i=0;i<3;i++) { koch(n,leng); t.turn(-120); } } • 將3個科赫曲線以-120°的角度連接起來,就形成1個白雪結晶狀的科赫島圖案。
練習問題62-2 十字繡 • 科赫曲線是以正三角形為基礎,而十字繡圖形則是以正方形為基礎。十字繡圖形的繪圖原理與科赫曲線相同,如要描繪n次的十字繡圖形,則只須描繪5個描n-1次的十字繡圖形即可,描繪的角度則按+90°、-90 ° 、-90 ° 、+90 °的順序操作。 // 十字繡圖形的遞迴動作程序 public void stech(int n,double leng) { if (n==0) t.move(leng); else { stech(n-1,leng);t.turn(-90); stech(n-1,leng);t.turn(90); stech(n-1,leng);t.turn(90); stech(n-1,leng);t.turn(-90); stech(n-1,leng); } } public void paint(Graphics g) { t=new Turtle(g); int k,n=4; // 十字繡圖形的次數 double leng=2; // 0次的長度 t.setpoint(100,150);t.setangle(0); for (k=1;k<=4;k++) { stech(n,leng); t.turn(90); } }
8-8 遞迴繪圖Ⅱ • 描繪樹木曲線 樹木曲線的描繪規則如下: ‧ 0次的樹木曲線為長度為l的直線 ‧ 1次的樹木曲線為2支長度為l/2的分支,兩線的夾角為90° ‧ 2次的樹木曲線為2支長度為l/4的分支,兩線的夾角為90 ° 樹木的分支共有4支。 此外分支的縮小率、伸展角度不一定非得1/2與90 °不可。
樹木曲線之描繪演算法 n次的樹木曲線之描繪演算法的大致內容如下: (1)從(x0,y0)位置起以角度a畫出長度為leng的分支,並將畫好以後的終點座標設為新的(x0,y0)位置 (2)將n-1次的右邊的樹遞迴呼叫出來 (3)將n-1次的左邊的樹遞迴呼叫出來
void tree(int n,double x0,double y0,double leng,double angle) { if (n==0) return; t.setpoint(x0,y0);t.setangle(angle); t.move(leng); x0=t.LPX;y0=t.LPY; // 取得現在位置 tree(n-1,x0,y0,leng/scale,angle-branch); tree(n-1,x0,y0,leng/scale,angle+branch); } public void paint(Graphics g) { t=new Turtle(g); int n; double x0,y0,leng,angle; n=8; // 分支的次數 x0=200.0;y0=50.0; // 根的位置 leng=100.0; // 分支的長度 angle=90.0; // 分支的角度 scale=1.4; // 分支的伸展率 branch=20.0; // 分支的分歧角 tree(n,x0,y0,leng,angle); }
練習問題63 樹木曲線II • 描繪樹木曲線時也可以用正方形來取代直線。 • 如圖8.39所示,在正方形的分支中以45?的角度描繪出的子正方形分支。 • 如圖8.40所示,以位置為起點,按(1)→(2)→(3)→(4)的順序描繪正方形。移到右邊分支後的新位置為: • 此外,由於右邊的分支返回時的主幹的位置為、角度為angle、長度為leng,因此從此點移到左邊分支後的新位置為:
void ctree(int n,double x0,double y0,double leng,double angle) { final double rd=3.14159/180; int k; if (n==0) return; t.setpoint(x0,y0);t.setangle(angle); for (k=1;k<=4;k++) { // 描繪正方形 t.turn(90); t.move(leng); } // 右邊分支 ctree(n-1,x0+leng*Math.cos((angle-45)*rd)/Math.sqrt(2.0), y0+leng*Math.sin((angle-45)*rd)/Math.sqrt(2.0), leng/Math.sqrt(2.0),angle-45); // 左邊分支 ctree(n-1,x0+Math.sqrt(2.0)*leng*Math.cos((angle+45)*rd), y0+Math.sqrt(2.0)*leng*Math.sin((angle+45)*rd), leng/Math.sqrt(2.0),angle+45); } public void paint(Graphics g) { t=new Turtle(g); int n=9; // 分支的次數 double x0=0.0,y0=0.0, // 根的位置 leng=50.0, // 分支的長度 angle=90.0; // 分支的角度 t.window(-200,-200,200,200); t.view(0,0,400,400); ctree(n,x0,y0,leng,angle); }