前言

碎形(fractal)是數學中最迷人的概念之一。一個簡單的規則不斷重複,就能產生無限複雜的結構。海岸線的曲折、花椰菜的分支、雪花的對稱——自然界處處是碎形。

當我第一次用程式畫出 Koch 雪花的時候,立刻就想:「這個能不能用雷切做出來?」答案是可以,但有一些有趣的挑戰。螢幕上的碎形可以無限放大,但實體化有尺寸限制;數學上的碎形有無限細節,但雷切有精度極限。怎麼在這些限制中找到最佳平衡點,就是這篇文章要探討的主題。

我會介紹幾種經典碎形曲線的 SVG 生成方法,討論迭代深度與切割精度的取捨,最後分享如何把碎形做成項鍊、耳環等飾品。

經典碎形曲線

Koch 曲線與 Koch 雪花

Koch 曲線是最經典的碎形之一。它的生成規則非常簡單:

  1. 從一條直線段開始
  2. 將線段三等分
  3. 以中間那段為底邊,向外畫一個等邊三角形
  4. 移除底邊
  5. 對所有線段重複步驟 2-4

把三條 Koch 曲線組成等邊三角形,就是 Koch 雪花。

import math

def koch_curve(start, end, depth): """遞迴生成 Koch 曲線的點序列""" if depth == 0: return [start, end]

# 三等分點 dx = end[0] - start[0] dy = end[1] - start[1]

p1 = (start[0] + dx/3, start[1] + dy/3) p3 = (start[0] + 2dx/3, start[1] + 2dy/3)

# 等邊三角形頂點 angle = math.atan2(dy, dx) - math.pi / 3 length = math.sqrt(dx2 + dy2) / 3 p2 = (p1[0] + length * math.cos(angle), p1[1] + length * math.sin(angle))

# 遞迴 points = [] points.extend(koch_curve(start, p1, depth - 1)[:-1]) points.extend(koch_curve(p1, p2, depth - 1)[:-1]) points.extend(koch_curve(p2, p3, depth - 1)[:-1]) points.extend(koch_curve(p3, end, depth - 1))

return points

def koch_snowflake(center, size, depth): """生成 Koch 雪花的所有點""" # 等邊三角形的三個頂點 angles = [-math.pi/2, -math.pi/2 + 2math.pi/3, -math.pi/2 + 4math.pi/3] vertices = [(center[0] + size * math.cos(a), center[1] + size * math.sin(a)) for a in angles]

all_points = [] for i in range(3): start = vertices[i] end = vertices[(i + 1) % 3] segment = koch_curve(start, end, depth) all_points.extend(segment[:-1]) # 避免重複點

all_points.append(all_points[0]) # 閉合 return all_points

Hilbert 曲線

Hilbert 曲線是一種空間填充曲線(space-filling curve)——它在極限情況下會經過正方形內的每一個點。它的美在於只用一條不交叉的連續線就填滿整個空間。

def hilbert_curve(order, size=100):
    """使用 L-system 生成 Hilbert 曲線"""
    # L-system 規則
    # A → -BF+AFA+FB-
    # B → +AF-BFB-FA+

def generate_string(order): s = 'A' for _ in range(order): new_s = '' for c in s: if c == 'A': new_s += '-BF+AFA+FB-' elif c == 'B': new_s += '+AF-BFB-FA+' else: new_s += c s = new_s return s

instructions = generate_string(order)

# 執行繪圖指令 x, y = 0, 0 angle = 0 step = size / (2**order - 1) if order > 0 else size points = [(x, y)]

for c in instructions: if c == 'F': x += step * math.cos(math.radians(angle)) y += step * math.sin(math.radians(angle)) points.append((x, y)) elif c == '+': angle += 90 elif c == '-': angle -= 90

return points

Sierpinski 三角形

Sierpinski 三角形是另一個經典碎形,適合做成鏤空飾品:

def sierpinski_triangle(vertices, depth):
    """生成 Sierpinski 三角形的所有三角形"""
    if depth == 0:
        return [vertices]

# 三邊中點 mid01 = ((vertices[0][0] + vertices[1][0]) / 2, (vertices[0][1] + vertices[1][1]) / 2) mid12 = ((vertices[1][0] + vertices[2][0]) / 2, (vertices[1][1] + vertices[2][1]) / 2) mid02 = ((vertices[0][0] + vertices[2][0]) / 2, (vertices[0][1] + vertices[2][1]) / 2)

# 遞迴三個子三角形(中間的三角形被移除) triangles = [] triangles.extend(sierpinski_triangle( [vertices[0], mid01, mid02], depth - 1)) triangles.extend(sierpinski_triangle( [mid01, vertices[1], mid12], depth - 1)) triangles.extend(sierpinski_triangle( [mid02, mid12, vertices[2]], depth - 1))

return triangles

Dragon 曲線

Dragon 曲線(龍曲線)摺疊後的形狀像一條龍,非常適合做成掛飾:

def dragon_curve(order):
    """生成龍曲線的轉向序列"""
    turns = []
    for _ in range(order):
        turns = turns + [1] + [-t for t in reversed(turns)]

# 轉換為座標 x, y = 0, 0 dx, dy = 1, 0 points = [(x, y)]

for turn in turns: # 前進 x += dx y += dy points.append((x, y))

# 轉向 if turn == 1: # 左轉 dx, dy = -dy, dx else: # 右轉 dx, dy = dy, -dx

x += dx y += dy points.append((x, y))

return points

迭代深度 vs 切割精度

問題的本質

碎形的數學定義是無限迭代,但雷切機有最小特徵尺寸的限制。迭代太深,最小的線段會小於雷射光束寬度,導致線條糊在一起。

以 Koch 雪花為例,每次迭代會讓最短線段變為原來的 1/3:

迭代 0:線段長度 = L
迭代 1:線段長度 = L/3
迭代 2:線段長度 = L/9
迭代 3:線段長度 = L/27
迭代 4:線段長度 = L/81
迭代 5:線段長度 = L/243

如果初始邊長 L = 60mm:

| 迭代深度 | 最短線段 | 線段數量 | 適合雷切? |
|———|———|———|———–|
| 0 | 60mm | 3 | 太簡單 |
| 1 | 20mm | 12 | 太簡單 |
| 2 | 6.7mm | 48 | 可以 |
| 3 | 2.2mm | 192 | 理想 |
| 4 | 0.74mm | 768 | 極限 |
| 5 | 0.25mm | 3072 | 太細 |

結論:對於 60mm 的 Koch 雪花,迭代深度 3-4 是最佳範圍。

不同碎形的建議深度

| 碎形類型 | 作品尺寸 | 建議迭代深度 |
|———|———|————|
| Koch 雪花 | 50-80mm | 3-4 |
| Hilbert 曲線 | 50-80mm | 3-4 |
| Sierpinski 三角形 | 60-100mm | 4-5 |
| Dragon 曲線 | 50-80mm | 8-10 |

Dragon 曲線可以迭代更深,因為它的線段不會急速縮短——它是在空間中蜿蜒而非分岔。

SVG 匯出

完整的 Koch 雪花 SVG 生成器

import svgwrite
import math

def koch_snowflake_svg(filename, size=60, depth=3, stroke_w=0.2): """生成可直接雷切的 Koch 雪花 SVG"""

center = (size 1.2, size 1.2) points = koch_snowflake(center, size, depth)

# 計算邊界 xs = [p[0] for p in points] ys = [p[1] for p in points] min_x, max_x = min(xs) - 2, max(xs) + 2 min_y, max_y = min(ys) - 2, max(ys) + 2 w = max_x - min_x h = max_y - min_y

dwg = svgwrite.Drawing(filename, size=(f'{w}mm', f'{h}mm'), viewBox=f'{min_x} {min_y} {w} {h}')

# 繪製雪花輪廓 path_data = f'M {points[0][0]:.3f},{points[0][1]:.3f} ' for p in points[1:]: path_data += f'L {p[0]:.3f},{p[1]:.3f} '

dwg.add(dwg.path(d=path_data, fill='none', stroke='black', stroke_width=stroke_w))

# 吊環孔(如果做成掛飾) hole_y = min(ys) + 2 dwg.add(dwg.circle((center[0], hole_y), 1.5, fill='none', stroke='black', stroke_width=stroke_w))

dwg.save() print(f"Koch snowflake (depth={depth}) saved to {filename}") print(f" Size: {w:.1f} x {h:.1f} mm") print(f" Segments: {len(points) - 1}")

koch_snowflake_svg('koch_pendant.svg', size=30, depth=3)

Hilbert 曲線 SVG

def hilbert_svg(filename, order=4, size=50, line_width=0.8):
    """生成 Hilbert 曲線 SVG"""
    points = hilbert_curve(order, size)

# 偏移到正數座標 min_x = min(p[0] for p in points) min_y = min(p[1] for p in points) points = [(p[0] - min_x + 5, p[1] - min_y + 5) for p in points]

max_x = max(p[0] for p in points) + 5 max_y = max(p[1] for p in points) + 5

dwg = svgwrite.Drawing(filename, size=(f'{max_x}mm', f'{max_y}mm'), viewBox=f'0 0 {max_x} {max_y}')

# 繪製曲線 path_data = f'M {points[0][0]:.3f},{points[0][1]:.3f} ' for p in points[1:]: path_data += f'L {p[0]:.3f},{p[1]:.3f} '

dwg.add(dwg.path(d=path_data, fill='none', stroke='black', stroke_width=line_width))

# 外框(切割線) margin = 3 dwg.add(dwg.rect((margin, margin), (max_x - 2margin, max_y - 2margin), rx=2, ry=2, fill='none', stroke='red', stroke_width=0.2))

dwg.save()

hilbert_svg('hilbert_coaster.svg', order=4, size=60)

飾品設計

項鍊墜飾

Koch 雪花是最受歡迎的碎形項鍊設計。設計要點:

  • 尺寸:30-40mm 直徑最合適
  • 材料:3mm 壓克力或 1.5mm 不鏽鋼(需光纖雷切)
  • 吊環:在頂部留一個直徑 3mm 的小孔,穿過項鍊繩或跳環
  • 迭代深度:3 次迭代。太少沒有碎形感,太多會太脆弱
  • 表面處理:壓克力可以用火焰拋光邊緣,金屬可以做拋光或霧面

耳環

耳環要更輕更小:

  • 尺寸:15-25mm
  • 材料:1.5-2mm 壓克力最輕,木材也可以
  • 迭代深度:2-3 次(因為尺寸小,不需要太多層次)
  • 配件:黏上耳環鉤(可以在手工材料行買到)

成對耳環可以做鏡像設計(一個用碎形、一個用碎形的「負空間」),形成有趣的對比。

Hilbert 曲線胸針

Hilbert 曲線做成胸針效果很好,因為它填滿正方形的特性讓作品看起來很「完整」:

  • 尺寸:40-50mm 正方形
  • 材料:3mm 深色壓克力上雕刻白色曲線(反向雕刻)
  • 配件:背面黏上胸針底座

Sierpinski 三角形吊飾

Sierpinski 三角形天生適合鏤空設計——被移除的部分本來就是三角形的洞:

  • 切割外輪廓和所有被移除的三角形
  • 迭代 4 次的 Sierpinski 三角形有 27 個三角形孔
  • 看起來像蕾絲一樣精緻

進階:碎形飾品的結構強化

碎形形狀往往有很細的突出部分,容易折斷。幾個強化技巧:

  1. 加粗輪廓:在 SVG 中把線段寬度從理論上的零寬度加到 1-2mm,然後做 offset 處理
  2. 填充細節:最深層的碎形細節不做鏤空,改成表面雕刻
  3. 背板支撐:用一片完整的圓形或方形背板黏在碎形後面
  4. 樹脂灌注:在鏤空的碎形上灌注 UV 樹脂,既強化結構又增加光澤

小結

碎形飾品的魅力在於它們的「自相似性」——不管你從哪個距離看,都能發現重複的結構。這種數學之美以實體形式呈現,總是能引起旁人的好奇和讚嘆。

「這個圖案是什麼?」幾乎是每個看到碎形飾品的人的第一反應。而你可以藉此機會分享碎形的迷人世界——從 Koch 雪花的無限周長到 Hilbert 曲線的空間填充,每一個碎形都有一個精彩的故事。

延伸閱讀:

  • Benoit Mandelbrot, The Fractal Geometry of Nature — 碎形幾何的奠基之作
  • Daniel Shiffman, The Nature of Code — 用程式碼探索碎形和自然現象
  • Nervous System 的碎形珠寶作品 — 商業級碎形飾品的設計參考
  • L-system 線上模擬器 — 快速實驗不同的碎形規則