前言
M.C. Escher 的作品大概是很多人對「數學之美」的第一次覺醒。那些魚變成鳥、蜥蜴互相咬尾的鑲嵌圖案,既是嚴謹的數學結構,又充滿了想像力和幽默感。
鑲嵌(tessellation)本身是一個古老的數學主題——用形狀完全覆蓋平面、不重疊、不留間隙。從伊斯蘭建築的幾何磁磚到日本的和柄圖案,鑲嵌無所不在。而當你把鑲嵌圖案與雷射雕刻結合,就能創造出層次豐富的裝飾作品。
這篇文章會從正多邊形鑲嵌的數學開始,介紹 Escher 風格的變形技巧,然後用 p5.js 生成鑲嵌圖案,最後分享雷雕實作的經驗。
正多邊形鑲嵌
三種正則鑲嵌
能夠單獨鋪滿平面的正多邊形只有三種:
- 正三角形:每個頂點周圍有 6 個三角形(60° × 6 = 360°)
- 正方形:每個頂點周圍有 4 個正方形(90° × 4 = 360°)
- 正六邊形:每個頂點周圍有 3 個六邊形(120° × 3 = 360°)
為什麼只有這三種?因為正多邊形的內角必須能整除 360°:
正 n 邊形內角 = (n-2) × 180° / n
n=3: 60° → 360°/60° = 6 ✓
n=4: 90° → 360°/90° = 4 ✓
n=5: 108° → 360°/108° = 3.33... ✗
n=6: 120° → 360°/120° = 3 ✓
n=7: 128.6° → 不整除 ✗
...以此類推,都不行了
半正則鑲嵌(Archimedean Tiling)
如果允許混合使用不同的正多邊形,就能得到 8 種半正則鑲嵌。例如:
- (3, 6, 3, 6):三角形和六邊形交替排列
- (4, 8, 8):每個頂點周圍是一個正方形和兩個正八邊形
- (3, 3, 3, 3, 6):四個三角形加一個六邊形
這些組合在裝飾設計中非常實用,因為它們提供了比單一形狀更豐富的視覺變化。
Escher 變形技巧
基本原理
Escher 的鑲嵌魔法其實基於一個簡單的原理:對基本鋪排單元進行變形,同時保持鋪排的數學約束。
以正方形鑲嵌為例,最基本的變形步驟是:
- 在正方形的一條邊上畫一條自由曲線
- 將這條曲線平移到對邊(如果是平移對稱的鑲嵌)
- 在另一對邊上重複同樣的操作
- 最終得到的形狀仍然能完美鋪排
四種基本對稱操作
Escher 使用了四種對稱操作來創造鑲嵌:
- 平移(Translation):一邊的曲線直接平移到對邊
- 旋轉(Rotation):曲線繞頂點旋轉到相鄰邊
- 鏡射(Reflection):曲線鏡射到對邊
- 滑移鏡射(Glide Reflection):平移+鏡射的組合
實作:從正方形到魚
讓我用一個具體的例子示範。我們要把正方形變形成一條魚:
步驟一:取一個正方形
┌────────┐
│ │
│ │
│ │
└────────┘
步驟二:在上邊畫一條曲線(魚的背部輪廓)
╱╲ ╱╲
╱ ╲╱ ╲
│ │
│ │
└────────┘
步驟三:將上邊的曲線平移到下邊
╱╲ ╱╲
╱ ╲╱ ╲
│ │
│ │
╲ ╱╲ ╱
╲╱ ╲╱
步驟四:在左邊畫魚頭,平移到右邊成為魚尾
最終,這個變形後的形狀可以像正方形一樣完美鋪排平面。
用 p5.js 生成鑲嵌圖案
基本的正六邊形鑲嵌
先來一個基礎的六邊形鑲嵌,作為後續變形的起點:
// p5.js 正六邊形鑲嵌
const hexSize = 30;
function setup() {
createCanvas(800, 600);
noLoop();
}
function draw() {
background(255);
const w = hexSize * sqrt(3);
const h = hexSize * 2;
for (let row = -1; row < height / (h * 0.75) + 1; row++) {
for (let col = -1; col < width / w + 1; col++) {
let x = col * w;
let y = row h 0.75;
// 奇數行偏移半個寬度
if (row % 2 !== 0) {
x += w / 2;
}
drawHexagon(x, y, hexSize);
}
}
}
function drawHexagon(cx, cy, size) {
beginShape();
for (let i = 0; i < 6; i++) {
let angle = TWO_PI / 6 * i - PI / 6;
let x = cx + size * cos(angle);
let y = cy + size * sin(angle);
vertex(x, y);
}
endShape(CLOSE);
}
Escher 風格變形鑲嵌
接下來,我們在正方形鑲嵌的基礎上加入變形,用平移對稱創造出互鎖的形狀:
// Escher 風格平移鑲嵌
const tileSize = 60;
let deformation = [];
function setup() {
createCanvas(800, 600);
noLoop();
// 生成上邊的變形曲線(隨機但平滑)
const steps = 20;
for (let i = 0; i <= steps; i++) {
let t = i / steps;
// 用正弦波組合產生有機的曲線
let dx = t * tileSize;
let dy = 8 sin(t PI 2) + 5 sin(t PI 4);
deformation.push(createVector(dx, dy));
}
}
function draw() {
background(240);
for (let row = -2; row < height / tileSize + 2; row++) {
for (let col = -2; col < width / tileSize + 2; col++) {
let x = col * tileSize;
let y = row * tileSize;
// 交替著色
let colorIndex = (row + col) % 3;
if (colorIndex < 0) colorIndex += 3;
let colors = [
color(70, 130, 180), // 鋼藍
color(180, 100, 60), // 赤褐
color(100, 160, 90) // 橄欖綠
];
fill(colors[colorIndex]);
stroke(0);
strokeWeight(0.5);
drawDeformedTile(x, y);
}
}
}
function drawDeformedTile(ox, oy) {
beginShape();
// 上邊(使用變形曲線)
for (let p of deformation) {
vertex(ox + p.x, oy + p.y);
}
// 右邊(使用同一條變形曲線,旋轉90度)
for (let i = 0; i < deformation.length; i++) {
let p = deformation[i];
let t = i / (deformation.length - 1);
vertex(ox + tileSize + p.y, oy + t * tileSize);
}
// 下邊(平移自上邊)
for (let i = deformation.length - 1; i >= 0; i--) {
let p = deformation[i];
vertex(ox + p.x, oy + tileSize + p.y);
}
// 左邊(平移自右邊)
for (let i = deformation.length - 1; i >= 0; i--) {
let p = deformation[i];
let t = i / (deformation.length - 1);
vertex(ox + p.y, oy + t * tileSize);
}
endShape(CLOSE);
}
加入細節讓形狀成為「生物」
Escher 的高明之處在於,他不只是讓形狀互鎖,還讓每個形狀看起來像某種可辨識的生物。要達到這個效果:
- 先決定你想要的生物(魚、鳥、蜥蜴等)
- 設計變形曲線時,讓輪廓暗示該生物的特徵
- 在形狀內部加上眼睛、翅膀等細節
- 用不同顏色區分相鄰的形狀
// 在形狀內部加入細節(以魚為例)
function addFishDetails(ox, oy, facing) {
push();
translate(ox + tileSize/2, oy + tileSize/2);
if (facing < 0) scale(-1, 1);
// 眼睛
fill(255);
ellipse(-tileSize 0.2, -tileSize 0.1, 6, 6);
fill(0);
ellipse(-tileSize 0.2, -tileSize 0.1, 3, 3);
// 鰭
noFill();
stroke(0, 100);
arc(0, 0, tileSize 0.3, tileSize 0.3, -PI/4, PI/4);
// 鱗片紋理
for (let i = 0; i < 3; i++) {
arc(tileSize 0.1 i, 0, 8, 8, -PI/3, PI/3);
}
pop();
}
從 p5.js 到 SVG
要將 p5.js 生成的圖案用於雷雕,需要匯出 SVG 格式。可以使用 p5.js-svg 函式庫:
// 在 HTML 中引入 p5.svg.js
// <script src="p5.svg.js"></script>
function setup() {
createCanvas(800, 600, SVG); // SVG 模式
noLoop();
}
function draw() {
// ... 繪製鑲嵌圖案 ...
// 完成後儲存
save('tessellation.svg');
}
或者,如果你偏好 Python,可以直接用 svgwrite 生成:
import svgwrite
import math
def hex_tessellation_svg(filename, cols=10, rows=8, size=15):
w = size * math.sqrt(3)
h = size * 2
canvas_w = cols * w + w/2
canvas_h = rows h 0.75 + h * 0.25
dwg = svgwrite.Drawing(filename,
size=(f'{canvas_w}mm', f'{canvas_h}mm'),
viewBox=f'0 0 {canvas_w} {canvas_h}')
for row in range(rows):
for col in range(cols):
cx = col * w + (w/2 if row % 2 else 0)
cy = row h 0.75 + size
points = []
for i in range(6):
angle = math.pi / 3 * i - math.pi / 6
px = cx + size * math.cos(angle)
py = cy + size * math.sin(angle)
points.append((px, py))
dwg.add(dwg.polygon(points,
fill='none', stroke='black',
stroke_width=0.15))
dwg.save()
hex_tessellation_svg('hex_tess.svg')
雷雕鑲嵌的實作技巧
線條粗細與材料
雷雕鑲嵌圖案時,線條的粗細很重要:
- 密度板上雕刻:建議線寬 0.1-0.2mm(向量模式)
- 壓克力上雕刻:可以稍粗一點,0.2-0.3mm
- 紙張上切割:鑲嵌的每個形狀可以切割出來,用不同顏色的紙重新拼貼
層次感的營造
單純的線條雕刻會顯得平淡。加入層次感的方法:
- 不同深度的雕刻:輪廓線用較高功率(深雕),內部細節用較低功率(淺雕)
- 填充雕刻:某些區域用柵格填充,產生色調差異
- 鏤空切割:交替的形狀一個切穿、一個保留,背後放上對比色材料
尺寸考量
鑲嵌圖案的最小特徵尺寸取決於雷切的精度:
- CO2 雷切:最小特徵約 0.3mm
- 光纖雷切:最小特徵約 0.1mm
- 建議鑲嵌單元不要小於 10mm,否則細節會糊掉
範例作品:六邊形鑲嵌燈罩
我做過一個蠻滿意的作品——用六邊形鑲嵌設計的木質燈罩。做法是:
- 用 Python 生成六邊形鑲嵌的 SVG
- 在每個六邊形內部加入不同的幾何圖案(有些鏤空、有些雕刻)
- 用 3mm 樺木合板雷切
- 將多片拼接成圓柱形燈罩
- 內部放 LED 燈條
光線透過鏤空的六邊形投射在牆壁上,形成漂亮的光影圖案。這是鑲嵌設計的一個很棒的應用場景。
小結
鑲嵌圖案是數學、藝術和工藝的完美交會。從基本的正多邊形鋪排,到 Escher 風格的生物變形,再到雷雕的實體化,每一步都有足夠的深度值得探索。
我特別喜歡用程式生成鑲嵌的過程——寫幾十行程式碼就能產生無限延伸的圖案,這本身就是一種創作的快感。
延伸閱讀:
- Craig Kaplan 的論文集 — 計算鑲嵌設計的學術基礎
- Visions of Symmetry by Doris Schattschneider — Escher 鑲嵌作品的完整分析
- 阿爾罕布拉宮(Alhambra)的磁磚圖案 — 鑲嵌藝術的歷史巔峰
- GeoGebra 的鑲嵌工具 — 互動式探索鑲嵌的好工具