L-системи та turtle graphics
Система Ліндермаєра знову й знову переписує короткий рядок за жменькою правил, а «черепаха» — курсор, що пам'ятає позицію й напрямок — перетворює результат на папороті, дерева, криву дракона й мережі вулиць міста.
1. Що таке L-система?
Систему Ліндермаєра (L-система) представив у 1968 році біолог Арістід Ліндермаєр для моделювання росту водоростей і рослин клітина за клітиною. Основна ідея оманливо проста: почати з одного символу (аксіоми) і багаторазово замінювати кожен символ рядка на довший рядок за фіксованим набором правил породження. Після кількох ітерацій один символ виростає у рядок довжиною тисячі символів — і якщо інтерпретувати цей рядок як інструкції для малювання, отримуємо впізнавану рослину, сніжинку чи криву.
Магія в тому, що L-системи контекстно-незалежні (у найпростішому варіанті): правило переписування символу не зважає на те, що його оточує, проте сукупний результат все одно відтворює самоподібне розгалуження, яке ми бачимо у справжніх папоротях і деревах, бо саме біологічне зростання здебільшого є повторюваним локальним правилом, застосованим у кожній точці росту.
L-системи — універсальний генератор фракталів, тісно пов'язаний з системами ітерованих функцій (IFS). Сніжинка Коха та крива дракона — обидва класичні фрактали — мають однаково чисті визначення через L-систему, поряд з більш органічними формами, як-от папороть Барнслі.
2. Переписування рядків: аксіома та правила
L-систему визначають три речі: алфавіт символів, аксіома (початковий рядок) та набір правил породження, що зіставляють кожен символ із рядком заміни. Розглянемо найпростіший приклад:
Правила: A → AB, B → A
Застосування правил покоління за поколінням:
Пок. 1: AB
Пок. 2: ABA
Пок. 3: ABAAB
Пок. 4: ABAABABA
Довжина рядка точно слідує послідовності Фібоначчі — нагадування, що L-системи, патерни росту і числові послідовності часто є однією й тією самою ідеєю в різному вбранні.
3. Turtle graphics: від рядка до пікселів
Рядок літер — ще не картинка. Turtle graphics (з мови Logo Сеймура Пейперта, 1967) надає значення кожному символу, інтерпретуючи рядок як послідовність команд для уявної черепахи, яка носить позицію (x, y) і кут напрямку:
y += sin(heading) · stepLength
drawLine(prevX, prevY, x, y)
Прогнати черепаху через цілий переписаний рядок, символ за символом, — і відрізки, які вона малює, окреслюють форму, закодовану в граматиці.
4. Стандартний алфавіт черепахи
| Символ | Значення |
|---|---|
F, G |
Рух вперед на один крок, з малюванням лінії |
f |
Рух вперед на один крок без малювання («стрибок») |
+ |
Повернути ліворуч (проти годинникової) на фіксований кут δ |
- |
Повернути праворуч (за годинниковою) на фіксований кут δ |
[ |
Покласти поточний стан (позицію + напрямок) у стек |
] |
Витягнути стан зі стека і зробити його поточним |
Лише два числа контролюють усю візуальну родину: довжина
кроку (наскільки далеко рухає кожен F) і
кут повороту δ (наскільки різко + та
- обертають напрямок). Зміна δ з 20° на 25° може
перетворити охайний геометричний патерн на дикий органічний.
5. Розгалуження через стек: [ та ]
Рослини розгалужуються — стебло розділяється на два, кожне з яких
може розділитися знову. Одна черепаха може дивитися лише в одному
напрямку водночас, тож розгалуження моделюється стеком:
[ зберігає поточну позицію та напрямок черепахи,
черепаха малює бічну гілку, а ] відновлює збережений
стан, щоб основне стебло продовжилося саме там, де зупинилося.
Правило: F → F[+F]F[-F]F
δ = 25°
Після 4-5 поколінь це триряткове правило дає впізнавано кущисту силует — доказ того, що більшість візуальної складності рослини не закодована явно, а виникає з короткого правила, застосованого рекурсивно.
6. Класичні приклади
Крива Коха (фрактальна берегова лінія)
Правило: F → F+F--F+F
δ = 60°
Крива дракона
Правила: X → X+YF+, Y → -FX-Y
δ = 90°
Трикутник Серпінського (arrowhead)
Правила: A → B-A-B, B → A+B+A
δ = 60°
Папороть у стилі Барнслі
Правила: X → F+[[X]-X]-F[-FX]+X, F → FF
δ = 25°
Справжні рослини не є ідеально самоподібними. Поширений трюк — стохастична L-система: дати символу два чи більше альтернативних правил заміни й обирати між ними випадково щоразу (з вагою за ймовірністю). Це руйнує механічну регулярність і дає набагато переконливіші, природоподібні варіації між гілками.
7. Псевдокод
Генерація й малювання L-системи у двох чітких фазах:
// Фаза 1: переписування рядка
function rewrite(axiom, rules, generations):
s = axiom
for g in range(generations):
s = join(rules[c] or c for c in s)
return s
// Фаза 2: інтерпретація черепахою
function drawTurtle(str, stepLength, angleDeg):
x, y, heading = 0, 0, -90 // старт напрямом вгору
stack = []
for ch in str:
if ch in "FG":
nx = x + cos(heading) * stepLength
ny = y + sin(heading) * stepLength
drawLine(x, y, nx, ny)
x, y = nx, ny
elif ch == "f":
x += cos(heading) * stepLength
y += sin(heading) * stepLength
elif ch == "+":
heading += angleDeg
elif ch == "-":
heading -= angleDeg
elif ch == "[":
stack.push({x, y, heading})
elif ch == "]":
{ x, y, heading } = stack.pop()
Зверніть увагу на розділення відповідальностей: rewrite
нічого не знає про малювання, а drawTurtle нічого не
знає про правила граматики. Той самий інтерпретатор черепахи
малює криву Коха, папороть чи криву дракона — змінюється лише
аксіома, правила та кут.
🐢 Спробувати симуляцію L-системи
Обирайте з 8 пресетів — крива Коха, крива дракона, arrowhead Серпінського, папороть Барнслі та інші — і налаштовуйте довжину кроку, кут та кількість поколінь наживо.
Відкрити симуляцію →