分形透過簡單規則重複套用,產生無限複雜的自我相似結構。VC++ 配合 `CDC` 繪圖裝置,輕鬆實作經典分形。本文提供四種分形的完整遞迴函式與幾何計算,可直接整合至 `OnDraw` 或按鈕事件中。
// 於 OnDraw 或繪圖函式中取得 CDC*
void DrawFractals(CDC* pDC) {
// 科赫曲線
Koch(pDC, 100, 300, 500, 300, 5);
// 謝爾賓斯基三角形
Sierpinski(pDC, 300, 50, 100, 400, 500, 400, 5);
// 分形矩形
FractalRect(pDC, 100, 100, 400, 300, 4);
// 分形樹
FractalTree(pDC, 300, 400, 300, 100, 6);
}
將線段三等分,中段替換為等邊三角形兩側,遞迴套用。
void Koch(CDC* pDC, int x1, int y1, int x2, int y2, int n) {
if (n <= 0) return;
int x3 = x1 + (x2 - x1) / 3;
int y3 = y1 + (y2 - y1) / 3;
int x4 = x1 + (x2 - x1) * 2 / 3;
int y4 = y1 + (y2 - y1) * 2 / 3;
// 等邊三角形頂點 (向上凸)
int x5 = x3 + (x4 - x3) / 2 + (int)(sqrt(3.0) * (y4 - y3) / 2);
int y5 = y3 - (int)(sqrt(3.0) * (x4 - x3) / 2) + (y4 - y3) / 2;
if (n == 1) {
pDC->MoveTo(x1, y1);
pDC->LineTo(x3, y3);
pDC->LineTo(x5, y5);
pDC->LineTo(x4, y4);
pDC->LineTo(x2, y2);
} else {
Koch(pDC, x1, y1, x3, y3, n-1);
Koch(pDC, x3, y3, x5, y5, n-1);
Koch(pDC, x5, y5, x4, y4, n-1);
Koch(pDC, x4, y4, x2, y2, n-1);
}
}
幾何關鍵:旋轉向量 `(x4-x3, y4-y3)` 正負 60° 得到頂點。第 5 階段已極細緻,建議 `n <= 6`。
三角形連接三邊中點形成中央倒三角形移除,對三個角遞迴。
void Sierpinski(CDC* pDC, int x1, int y1, int x2, int y2, int x3, int y3, int n) {
if (n <= 0) return;
int x12 = (x1 + x2) / 2, y12 = (y1 + y2) / 2;
int x23 = (x2 + x3) / 2, y23 = (y2 + y3) / 2;
int x31 = (x3 + x1) / 2, y31 = (y3 + y1) / 2;
// 畫中央倒三角形邊框 (可選)
pDC->MoveTo(x12, y12); pDC->LineTo(x23, y23);
pDC->MoveTo(x23, y23); pDC->LineTo(x31, y31);
pDC->MoveTo(x31, y31); pDC->LineTo(x12, y12);
if (n == 1) {
// 畫最小三角形實心或邊框
pDC->MoveTo(x12, y12); pDC->LineTo(x23, y23);
pDC->LineTo(x31, y31); pDC->LineTo(x12, y12);
} else {
// 三個角遞迴
Sierpinski(pDC, x1, y1, x31, y31, x12, y12, n-1); // 左上
Sierpinski(pDC, x12, y12, x2, y2, x23, y23, n-1); // 右上
Sierpinski(pDC, x31, y31, x23, y23, x3, y3, n-1); // 底部
}
}
矩形內嵌旋轉正方形,對四個小矩形遞迴。
void FractalRect(CDC* pDC, int x1, int y1, int x2, int y2, int n) {
if (n <= 0) return;
int x3 = x1 + (x2 - x1) / 3, y3 = y1 + (y2 - y1) / 3;
int x4 = x1 + (x2 - x1) * 2 / 3, y4 = y1 + (y2 - y1) * 2 / 3;
// 內嵌正方形四頂點 (逆時針 90° 旋轉)
int x5 = x3 + (y4 - y3), y5 = y3 - (x4 - x3);
int x6 = x4 - (y4 - y3), y6 = y4 + (x4 - x3);
pDC->MoveTo(x1, y1); pDC->LineTo(x3, y3);
pDC->MoveTo(x4, y4); pDC->LineTo(x2, y2);
if (n == 1) {
pDC->MoveTo(x1, y1); pDC->LineTo(x3, y3);
pDC->LineTo(x5, y5); pDC->LineTo(x6, y6);
pDC->LineTo(x4, y4);
} else {
FractalRect(pDC, x3, y3, x5, y5, n-1);
FractalRect(pDC, x5, y5, x6, y6, n-1);
FractalRect(pDC, x6, y6, x4, y4, n-1);
}
}
主幹分叉為三支 (中、左、右),角度 ±150°,長度遞減。
const double PI = 3.14159265358979323846;
void FractalTree(CDC* pDC, int x1, int y1, int x2, int y2, int n) {
if (n <= 0) return;
int x3 = x1 + (x2 - x1) / 3;
int y3 = y1 + (y2 - y1) / 3;
// 向量 (x1-x3, y1-y3) 旋轉 ±150° (5π/6)
double cos150 = cos(5*PI/6), sin150 = sin(5*PI/6);
int dx = x1 - x3, dy = y1 - y3;
int x4 = x3 + (int)(dx * cos150 - dy * sin150);
int y4 = y3 + (int)(dx * sin150 + dy * cos150);
int x5 = x3 + (int)(dx * cos150 + dy * sin150);
int y5 = y3 - (int)(dx * sin150 - dy * cos150);
pDC->MoveTo(x1, y1); pDC->LineTo(x2, y2); // 主幹
if (n == 1) {
pDC->MoveTo(x3, y3); pDC->LineTo(x4, y4);
pDC->MoveTo(x3, y3); pDC->LineTo(x5, y5);
} else {
FractalTree(pDC, x3, y3, x2, y2, n-1); // 中央延伸
FractalTree(pDC, x3, y3, x4, y4, n-1); // 左分支
FractalTree(pDC, x3, y3, x5, y5, n-1); // 右分支
}
}