底下秀出 4 種圖:
1.Koch curve
2.Sierpinski’s Gasket
3.Tree curve
4.Julia set
其中,sierpinski 外緣線需自行接上,
Julia set 觀察窗需手動調整:
#include <windows.h>
#include <math.h>
#include <complex>
#define RGB(r,g,b) (b<<16)|(g<<8)|r
enum {W=800, H=600};
HDC hdc;
HANDLE hwnd, hIn, hOut;
namespace Turtle
{
//--------------------- 作圖 -----------------------
const float rd = 3.1415927/180.0;
int* pic, X, Y, A, ang, step, C; //定義: 畫布, 座標, 角度, 步距, 顏色
float scale;
float branch;
void clear ()
{
int i = W*H; while (i--) pic[i] = 0;
}
void show () //注意: 顯示器色彩格式需為 32-bits
{
BITMAPINFO info = {{40, W, -H, 1,32,0,0,0,0,0,0}, {{0}}};
SetDIBitsToDevice
(hdc, 0,0,W,H, 0,0,0,H, pic, &info, DIB_RGB_COLORS);
}
void set (int x, int y, int d=0, int a=0) {X=x; Y=y; ang=a; step=d;}
void setxy (int x, int y) {set(x,y);}
void pixel (int x, int y, int c) //繪點
{
if (x<0 || y<0 || x>=W || y>=H) return;
pic[y*W+x] = c;
}
void line (int x, int y, int x2, int y2, int c=0xffffff)
{
int dx= x2-x, dy= y2-y, dirx=1, diry=1, b=0;
if (dx < 0) {dx = -dx; dirx= -1;}
if (dy < 0) {dy = -dy; diry= -1;}
pixel (x,y,c);
if (dx<=1 && dy<=1) return; //兩點間隔太小,不予處理
if (dx < dy) { //以Y軸為基準繪製線段
int hy= dy>>1, dotn= dy-2, n=1;
do {b += dx; if (b > hy) {b -= dy; x += dirx;}
y += diry; pixel (x,y,c); n++;
}while (dotn--);
}else{ //以X軸為基準繪製線段
int hx= dx>>1, dotn= dx-2, n=1;
do {b += dy; if (b > hx) {b -= dx; y += diry;}
x += dirx; pixel (x,y,c); n++;
}while (dotn--);
}
}
void move (int n, int c=0xffffff)
{
int x = n* cos (rd*A), y = n* sin (rd*A);
line (X, Y, X+x, Y+y, c);
X+=x; Y+=y;
}
void mov (int c=0xffffff) {move (step,c);}
void turn (int a)
{
A+=a;
if (A>360) A-=360;
else if (A<360) A+=360;
}
//--------------------- 碎形 -----------------------
void koch (int n)
{
if (0==n) {mov(); return;}
koch (n-1); turn (-60);
koch (n-1); turn (120);
koch (n-1); turn (-60);
koch (n-1);
}
void sierpinski (int n)
{
if (0==n) {mov(); return;}
sierpinski (n-1); turn(-120);
sierpinski (n-1); turn(120);
sierpinski (n-1); turn(120);
sierpinski (n-1); turn(-120);
sierpinski (n-1);
}
void tree (int n, int x, int y, float len, float ang)
{
if (0==n) return;
setxy(x,y); A=-ang; move(len); x=X; y=Y;
tree (n-1, x,y, len/scale, ang-branch);
tree (n-1, x,y, len/scale, ang+branch);
}
void julia (int n)
{
// z = x+iy, F(z) = z^2+C, |F(z)| = x^2+y^2
int i, k;
float v,xp,yp, x0,y0,x1,y1,x,y,z,cx,cy;
cx=-0.5; cy=0.5;
for (x0=-2.0; x0<=2.0; x0+=0.005)
for (y0=-2.0; y0<=2.0; y0+=0.005){
for (x = x0, y = y0, k = 0;
x*x+y*y<4.0 && k<n; ++k){ //迭代 n 次,直到數列發散
x1 = x*x - y*y + cx;
y1 = 2*x*y + cy;
x = x1; y = y1;
}
xp = W/4*x0+W/2;
yp = -H/4*y0+H/2;
v = x*x + y*y;
if (v < 4.0) {
i = v*64;
pixel (xp, yp, RGB (100,220,i));
}else
pixel (xp, yp, RGB (30,100,200));
}
}
};
using namespace Turtle;
int main (/* daviddr 081020 */)
{
SetConsoleTitle (L"Conso");
hdc = GetDC (FindWindowW (0, L"Conso"));
hIn = GetStdHandle (STD_INPUT_HANDLE);
hOut = GetStdHandle (STD_OUTPUT_HANDLE);
SMALL_RECT size = {0, 0, W-1, H-1};
SetConsoleWindowInfo (hOut, TRUE, &size); //變更視窗大小
Turtle::pic = new int[W*H]; //畫布
clear ();
set (20,380, 8); koch (4);
show ();
system ("pause");
clear ();
set (20,380, 8); sierpinski (5);
show ();
system ("pause");
clear ();
scale = 1.3;
branch = 23;
tree (8, 200,380, 64, 80);
show ();
system ("pause");
clear();
julia(32);
show ();
system ("pause");
delete [] Turtle::pic;
DeleteDC (hdc);
CloseHandle (hIn);
CloseHandle (hOut);
return 0;
}
sierpinski 外緣線由小而大逐次爬行的作法,
很難排除累計誤差,無法形成正三角。
可改為由外而內,用內插方式來算落點:
private:
void sierpinski (int n, int x1, int y1, int x2, int y2, int x3, int y3)
{
if (0 == n) {
setxy (x1, y1);
mov(); turn (-120);
mov(); turn (-120);
mov(); turn (-120);
return;
}
sierpinski (n-1, x1,y1, x3,y1, (x1+x3)/2, (y1+y3)/2);
sierpinski (n-1, x3,y1, x2,y2, (x2+x3)/2, (y2+y3)/2);
sierpinski (n-1, (x1+x3)/2, (y1+y3)/2, (x2+x3)/2, (y2+y3)/2, x3, y3);
}
public:
void sierpin2 (int n) //自頂至底
{
int d = step * (1<<n);
sierpinski (n, X,Y, X+d,Y, X+d/2, int(float(Y)-1.72384*d/2));
}
下面是呼叫 koch (4), sierpin2 (5), tree (8,..) 的結果: