From Gossip@caterpillar

Computer Graphics: 二維海龜繪圖法

 

 


海龜繪圖法取名海龜並不是因為它的速度像烏龜,而是它繪圖的方式就像海龜前進的方式。

考慮海龜只能前進的情況(我也不知道海龜能不能倒著游?),在2D的情況下,也就是海龜只在一個XY平面運動的情況下,海龜的運動基本上只有前進與轉彎兩個動作,由此開始聯想,定出海龜繪圖法的幾個基本方法:

  • 設定海龜起始位置
也就是將海龜直接放至某個(x, y)座標,而不進行任何的繪製動作。



  • 海龜由目前位置游至指定座標
由目前位置游至(x, y)位置,並在經過的路徑畫上直線。



  • 海龜由目前位置游動L長度
無論海龜目前的頭朝向哪一個角度,由目前位置與角度游動L長度,並在經過的路徑上畫上直線。



  • 設定海龜目前頭朝向的角度
角度的計算是以正X軸及頭所成的角度來計算。



  • 旋轉海龜的頭為指定的角度θ
即目前已成的角度 a 再加上θ,也就是與正X軸最後會成a+θ。



有了以上幾個基本方法,就可以進行各種圖案的繪圖,或是直接擴充海龜繪圖的方法,例如若要在目前的位置上繪製出一個正三角型,由海龜游動的方式, 可以如下繪製圖形:


如果要繪製實心的三角形,在撰寫方法時,在海龜游動的同時,記下三個頂點的座標,再呼叫API所提供的繪製實心多邊形方法即可。

如果要繪製點,可以如下結合兩個基本的海龜繪圖方法:


依照以下的描述,您可以自由撰寫並擴充您的海龜繪圖法,使繪圖的功能更加豐富,下面這個Java程式改寫自 Java 於演算法與資料結構之應用,在這邊僅提供程式碼作為參考,解說部份請徑自參考原書:

  • Turtle.java
package onlyfun.caterpillar.graphics;

// ---------------------------------- --
// * Turtle Graphics *
// ------------------------------------

import java.awt.*;

public class Turtle {
public double Angle, // current angle
LPX,LPY; // current position
private double WX1,WY1,WX2,WY2, // canvas coordination
VX1,VY1,VX2,VY2, // viewable area
FACTX,FACTY; // scale
private Graphics g;

public Turtle() {
Angle=0;
LPX=LPY=0;
}

public void setGraphics(Graphics g) {
this.g = g;
}

public Graphics getGraphics() {
return g;
}

public void view(int x1,int y1,int x2,int y2) {
g.setClip(x1,y1,x2-x1,y2-y1);
VX1=(double)x1;VY1=(double)y1;
VX2=(double)x2;VY2=(double)y2;
FACTX=(VX2-VX1)/(WX2-WX1);
FACTY=(VY2-VY1)/(WY2-WY1);
}

public void window(double x1,double y1,double x2,double y2) {
WX1=x1;WY1=y1;WX2=x2;WY2=y2;
FACTX=(VX2-VX1)/(WX2-WX1);
FACTY=(VY2-VY1)/(WY2-WY1);
}

public void warp(double l) {
LPX=LPX+l*Math.cos(Math.toRadians(Angle));
LPY=LPY+l*Math.sin(Math.toRadians(Angle));
}

public void move(double l) {
double x,y,x1,y1,x2,y2;
x=LPX+l*Math.cos(Math.toRadians(Angle));
y=LPY+l*Math.sin(Math.toRadians(Angle));
x1=(LPX-WX1)*FACTX+VX1;
y1=(WY2-LPY)*FACTY+VY1;
x2=(x-WX1)*FACTX+VX1;
y2=(WY2-y)*FACTY+VY1;
g.drawLine((int)x1,(int)y1,(int)x2,(int)y2);
LPX=x;LPY=y;
}

public void moveto(double x,double y) {
double x1,y1,x2,y2;
x1=(LPX-WX1)*FACTX+VX1;
y1=(WY2-LPY)*FACTY+VY1;
x2=(x-WX1)*FACTX+VX1;
y2=(WY2-y)*FACTY+VY1;
g.drawLine((int)x1,(int)y1,(int)x2,(int)y2);
LPX=x;LPY=y;
}

public void setpoint(double x,double y) {
LPX=x;LPY=y;
}

public void pset(double x,double y) {
setpoint(x,y);
moveto(x,y);
}

public void line(double x1,double y1,double x2,double y2) {
setpoint(x1,y1);
moveto(x2,y2);
}

public void turn(double a) {
Angle=Angle+a;
Angle=Angle-(int)Angle+((int)Angle % 360);
}

public void setangle(double a) {
Angle=a;
}
}