40
台台台台台 台台台台台台台台台台台 台 /一 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, T aiwan. 台台04-23287870 台台04-23108168 台台台台http://www.drmaster.com.tw 台台 Java 台台台台台台台台台 台台台台台 台台 PG20098 台台 台 台台 西 台台 台台台 / 台台台

台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

  • Upload
    thad

  • View
    45

  • Download
    0

Embed Size (px)

DESCRIPTION

書名 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. - PowerPoint PPT Presentation

Citation preview

Page 1: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

台北總公司/台北縣汐止市新台五路一段 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

書名 Java 於資料結構與演算法之實習應用

書號  PG20098原著 河西朝雄著譯者 周明憲 /徐堯譯

Page 2: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

第八章 學習重點 我們導入座標以便將點的位置顯示出來,各點的位置關係如在

直線上,則能以 1 次方程式呈現,各點如位於圓、橢圓、雙曲線、拋物線等曲線上,則能以 2 次方程式顯示出來。如此一來2 維圖形就能以某種方程式 (y=f(x)) 表示出來,因此以運算的方式就能解開圖形問題的解。這種學科稱為解析幾何學,其創始者為迪斯卡提 (Rene Descartes)

若將圖形以方程式表示出來,並以電腦加以解析的話,則以轉換座標方式進行的圖形平行移動、旋轉、放大與縮小等作業就能輕易的做到。此外, 3 維空間座標也因而能投影至 2 維平面。

但如要以解析方式將自然界存在的物體,比如複雜的海岸線表示出來還很困難。目前碎形幾何 (Fractals) 可以表示這種複雜的圖形。這是以遞迴方式將圖形表示出來的方法,又稱為遞迴繪圖 (recursive graphics) 。

Page 3: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-0 Java 的繪圖處理1 座標

Java 的畫面座標如右圖所示, y 軸座標是朝畫面的下方前進。由於這與數學座標的標示方式不同,因而採用圖 8.2所示的座標系統。邏輯座標中的 y 軸座標朝畫面的上方前進, Java 將此邏輯座標上的 (WX1,WY1)-(WX2,WY2) 區域(視窗)對應至畫面上的 (VX1,VY1)-(VX2,VY2) 區域(景象埠: view port )。

Page 4: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

1 座標 將視窗上的座標 (x,y) 描繪成景象埠的座標時,可用下列的計算式計算:

視窗的設定 window(x1,y1,x2,y2)將邏輯座標上的 (x1,y1)-(x2,y2)範圍設定成視窗。

景象埠的設定 view(x1,y1,x2,y2)將景象座標上的 (x1,y1)-(x2,y2)範圍設定成景象埠。

)12/()12( WXWXVXVXFACTX )12/()12( WYWYVYVYFACTX

…x 方向的倍率

1*)1( VXFACTXWXxpx 1*)2( VYFACTYyWYpy

…y 方向的倍率

Page 5: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

2 Turtle Graphics Library 下面為畫直線的 2個方法:(1) 設起點與終點(2) 設長度與方向 Java製作了move 、 turn

等 2個method ,使第 2個方法得以在 Java 上使用。這個方法也適合用來製作Turtle Graphics 。

用第 2個方法來畫直線時,先要以描繪的目前位置 (LPX,LPY) 與目前角 Angle為基點。

Page 6: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

指定長度的直線 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° 之內。

Page 7: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-1 move 與 turn 例題 56 多角形

描繪正三角形~正九角形只要如圖將move(1);turn(120); 執行 3 次,就可畫出正三角形。畫正 n角形時的旋轉角度為 360/n[°]。

Page 8: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

………………

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); } } } }

Page 9: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 56 螺旋圖案 反覆執行move 與 tur

n ,並同時慢慢的縮減直線的長度

將 turn 的角度設為 a ,逐漸縮減的長度設為 step ,然後反覆執行move 與 turn ,直至直線的長度等於 10 。

改變 step 與 a 的值,就能畫出不同的圖形。

Page 10: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 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; } } }

…………

Page 11: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-2 2 維座標轉換 某點 (x0, y0)經過各種轉換後,其各點 (x, y)

可分別以下列方式求出。

對稱移動對著與 y 軸平行的直線 x=a 進行對稱移動,則

相反的,若對著與 x 軸平行的直線進行對稱移動,則

0

00 2)(

yy

xaaxax

00

0

2)( ybbyby

xx

Page 12: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

平行移動將點 (x0, y0)往 x 軸及 y 軸方向分別平行移動m與 n ,則

00

0

nyy

mxx

旋轉移動將點 (x0, y0) 旋轉 θ,則

cossin

sincos

00

00

yxy

yxx

放大、縮小將點 (x0, y0) 的 x 軸方向放大 k 倍,y 軸方向放大 1倍,則

0

0

lyy

kxx

Page 13: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

例題 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); }

Page 14: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 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 點座標,對此長方形進行旋轉轉換與放大/縮小轉換作業,並將結果顯示出來

Page 15: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-3 幾何繪圖 例題 58 對稱圖案

以三角形的重心為中心,再將基本圖形旋轉,反覆製作同樣的圖案

美麗的幾何圖案很乍見之下極為複雜,但這些圖案實際上是由基本的圖形(直線、多角形等)反覆製作而成的,具有協調性與規律性。

以正三角形的重心為中心,將基本圖形旋轉 2 次,每次均旋轉 120°。由此作業產生的圖案會被收斂至正三角形之內,而形成 1個新的圖形。皆下來將這個三角形圖形的倒立圖橫向描繪,如此反覆操作。

Page 16: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

正三角形的重心為將三角形的高 h劃分成 2 : 1的點。而描繪的範圍則設為

h

mhm

3

2,

23,

2

因此在製作逆像時,不只要將 y 座標的符號反轉過來,而且還要進行h/3 的修正。此外還使用旗標 a 與 b,以便正像與逆像能夠一再交互產生。

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; } }

Page 17: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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個基本作業後,就可製圖了。

Page 18: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

(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 平面的點的座標

)cos()sin(

)sin()cos(

1

1

1

zxz

yy

zxx

)cos()sin(

)sin()cos(

112

112

12

zyz

zyy

xx

23

223

223

)cos()sin(

)sin()cos(

zz

yxy

yxx

)cos()sin(

)sin()cos(

)sin()cos(

)cos()sin(

)sin()cos(

223

223

112

12

1

1

1

yxy

yxx

zyy

xx

zxz

yy

zxx

Page 19: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

例題 59 軸測投影 設有 1幢房子的資料,將此房子以軸測投影顯示出來。房子的各點的資料存在如下

所示的陣列內。dat[0].f=-1:dat[0].x=80:dat[0].y=50:dat[0].z=100dat[0].f= 1:dat[1].x= 0:dat[1].y=50:dat[1].z=100

α=20° β=-45° γ=0° α=45° β=-45° γ=0° α=45° β=-45° γ=0°

Page 20: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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)); }

Page 21: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 59 透視 軸測投影是對著投影面投射平行光線,而透視則如

圖 8.20 所示,對著某點投射集中的光線。這點稱為投影中心(消失點),為了使透視轉換能夠更容易操作,我們將此點設在 z軸上。

在透視中,與投影中心相對的立體位置如有不同,則立體的感覺就不同,立體物若位於投影中心的上方,則透視的結果如同俯視立體物,反之,則透視的結果如同仰視立體物。

因此在透視中,除了將立體物旋轉的動作外,還加上了平行移動的操作。一般而言,看透視圖時,為了讓物體看起來很立體,只要旋轉 y 軸即可,不須旋轉 x 、 y 、 z等 3 軸。

Page 22: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

以下的計算式為透視的轉換計算式,為了簡化起見,旋轉時僅將 y 軸旋轉β?,並將 x 、 y 、 z方向的平行移動量設為 l 、m、 n ,將投影中心社為 z=-vp 。轉換後的點則設為透視成 z=0 平面( x-y 平面)的點。

先進行旋轉與平行移動作業,則

再將其透視成 z=0 平面,則

這種透視為 2 點透視,為最常用的方法。當β=0 時,則為單點透視(圖 8.21 )

myy

zxx

1

1 1)sin()cos(

lvpnvpzvpxh

hypy

hxpx

//)cos(/)sin(

/

/

1

1

Page 23: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); }

Page 24: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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 的直線(稜線)按某個旋轉角度分別畫在數個位置上。

)sin(

)cos(

rz

yy

rx

Page 25: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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);

Page 26: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); } }

Page 27: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-6 陰線處理前面已說明過將房子的資料以 3 維方式顯示的方法,而使用這種方法需要各頂點的資料,如要顯示複雜的立體物也需要大量的資料。 接下來我們將這種 3 維函數列出如下:

由於這項函數是以計算式的形式顯示,因此不需要用到資料,不用花太多心力就能畫出複雜的圖案。

2222 3coscos30 zxzxy

Page 28: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); } }

Page 29: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 61 max‧min 法 陰線處理是指將隱藏在面的後方之見不到的線消除之作業。

陰線處理的方法有很多種,本書使用非常簡單的max‧min 法。

如圖 8.29 所示,以max‧min法描繪圖形時,一定要從面向自己的一方開始畫起,而且這之後所描繪的點若位於之前所描繪的點群(位於與螢幕畫面同一 x 座標前的點群)的最大點與最小點之間(點群的內側),則不顯示該點,反之若位於這些點群之外,則顯示該點。

Page 30: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); } }

Page 31: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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 次細胞的模樣與成長的規則(+突變)的定義之不同,即可得到各種不同的物種。

Page 32: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

描繪科赫曲線 科赫曲線是由數學家科赫 (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 次的科赫曲線

Page 33: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); } }

Page 34: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 62-1 科赫島 將 3個科赫曲線

以 -120°的角度連接起來,就形成 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); } }

Page 35: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 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); } }

Page 36: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

8-8 遞迴繪圖Ⅱ 描繪樹木曲線樹木曲線的描繪規則如下:‧ 0 次的樹木曲線為長度為 l 的直線‧ 1 次的樹木曲線為 2支長度為 l/2

的分支,兩線的夾角為 90°

‧ 2 次的樹木曲線為 2支長度為 l/4的分支,兩線的夾角為 90 ° 樹木的分支共有 4支。此外分支的縮小率、伸展角度不一定非得 1/2 與 90 ° 不可。

Page 37: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

樹木曲線之描繪演算法n 次的樹木曲線之描繪演算法

的大致內容如下:(1)從 (x0,y0) 位置起以角度 a

畫出長度為 leng 的分支,並將畫好以後的終點座標設為新的 (x0,y0) 位置

(2) 將 n-1 次的右邊的樹遞迴呼叫出來

(3) 將 n-1 次的左邊的樹遞迴呼叫出來

Page 38: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); }

Page 39: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

練習問題 63 樹木曲線 II 描繪樹木曲線時也可以用正方形來

取代直線。 如圖 8.39 所示,在正方形的分支

中以 45?的角度描繪出的子正方形分支。

如圖 8.40 所示,以位置為起點,按 (1)→(2)→(3)→(4) 的順序描繪正方形。移到右邊分支後的新位置為:

此外,由於右邊的分支返回時的主幹的位置為、角度為 angle 、長度為 leng ,因此從此點移到左邊分支後的新位置為:

)45cos(2

' 00 angleleng

xx

)45sin(2

' 00 angleleng

yy

)45cos(2" 00 anglelengxx

)45sin(2" 00 anglelengyy

Page 40: 台北總公司/台北縣汐止市新台五路一段 112 號 10 樓 A 棟

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); }