Стаття Генеративне мистецтво · ≈ ⏱ 8 хв читання

L-системи та turtle graphics

Система Ліндермаєра знову й знову переписує короткий рядок за жменькою правил, а «черепаха» — курсор, що пам'ятає позицію й напрямок — перетворює результат на папороті, дерева, криву дракона й мережі вулиць міста.

Коротко: L-система будує складні форми з крихітного правила: береться один символ і багаторазово замінюється за фіксованим набором правил, поки рядок не виросте до тисяч символів. Потім «черепаха» читає цей рядок зліва направо — рухається, повертає й розгалужується за допомогою стека — і малює результат: папороті, дерева, криву Коха, криву дракона й трикутник Серпінського.

1. Що таке L-система?

Систему Ліндермаєра (L-система) представив у 1968 році біолог Арістід Ліндермаєр для моделювання росту водоростей і рослин клітина за клітиною. Основна ідея оманливо проста: почати з одного символу (аксіоми) і багаторазово замінювати кожен символ рядка на довший рядок за фіксованим набором правил породження. Після кількох ітерацій один символ виростає у рядок довжиною тисячі символів — і якщо інтерпретувати цей рядок як інструкції для малювання, отримуємо впізнавану рослину, сніжинку чи криву.

Магія в тому, що L-системи контекстно-незалежні (у найпростішому варіанті): правило переписування символу не зважає на те, що його оточує, проте сукупний результат все одно відтворює самоподібне розгалуження, яке ми бачимо у справжніх папоротях і деревах, бо саме біологічне зростання здебільшого є повторюваним локальним правилом, застосованим у кожній точці росту.

Фрактальний родич

L-системи — універсальний генератор фракталів, тісно пов'язаний з системами ітерованих функцій (IFS). Сніжинка Коха та крива дракона — обидва класичні фрактали — мають однаково чисті визначення через L-систему, поряд з більш органічними формами, як-от папороть Барнслі.

2. Переписування рядків: аксіома та правила

L-систему визначають три речі: алфавіт символів, аксіома (початковий рядок) та набір правил породження, що зіставляють кожен символ із рядком заміни. Розглянемо найпростіший приклад:

Ріст водоростей (оригінальний приклад Ліндермаєра) Аксіома: A
Правила: A → AB, B → A

Застосування правил покоління за поколінням:

Пок. 0: A
Пок. 1: AB
Пок. 2: ABA
Пок. 3: ABAAB
Пок. 4: ABAABABA

Довжина рядка точно слідує послідовності Фібоначчі — нагадування, що L-системи, патерни росту і числові послідовності часто є однією й тією самою ідеєю в різному вбранні.

3. Turtle graphics: від рядка до пікселів

Рядок літер — ще не картинка. Turtle graphics (з мови Logo Сеймура Пейперта, 1967) надає значення кожному символу, інтерпретуючи рядок як послідовність команд для уявної черепахи, яка носить позицію (x, y) і кут напрямку:

Рух вперед з малюванням x += cos(heading) · stepLength
y += sin(heading) · stepLength
drawLine(prevX, prevY, x, y)

Прогнати черепаху через цілий переписаний рядок, символ за символом, — і відрізки, які вона малює, окреслюють форму, закодовану в граматиці.

4. Стандартний алфавіт черепахи

Символ Значення
F, G Рух вперед на один крок, з малюванням лінії
f Рух вперед на один крок без малювання («стрибок»)
+ Повернути ліворуч (проти годинникової) на фіксований кут δ
- Повернути праворуч (за годинниковою) на фіксований кут δ
[ Покласти поточний стан (позицію + напрямок) у стек
] Витягнути стан зі стека і зробити його поточним

Лише два числа контролюють усю візуальну родину: довжина кроку (наскільки далеко рухає кожен F) і кут повороту δ (наскільки різко + та - обертають напрямок). Зміна δ з 20° на 25° може перетворити охайний геометричний патерн на дикий органічний.

5. Розгалуження через стек: [ та ]

Рослини розгалужуються — стебло розділяється на два, кожне з яких може розділитися знову. Одна черепаха може дивитися лише в одному напрямку водночас, тож розгалуження моделюється стеком: [ зберігає поточну позицію та напрямок черепахи, черепаха малює бічну гілку, а ] відновлює збережений стан, щоб основне стебло продовжилося саме там, де зупинилося.

Проста розгалужена рослина Аксіома: F
Правило: F → F[+F]F[-F]F
δ = 25°

Після 4-5 поколінь це триряткове правило дає впізнавано кущисту силует — доказ того, що більшість візуальної складності рослини не закодована явно, а виникає з короткого правила, застосованого рекурсивно.

6. Класичні приклади

Крива Коха (фрактальна берегова лінія)

Аксіома: F
Правило: F → F+F--F+F
δ = 60°

Крива дракона

Аксіома: FX
Правила: X → X+YF+, Y → -FX-Y
δ = 90°

Трикутник Серпінського (arrowhead)

Аксіома: A
Правила: A → B-A-B, B → A+B+A
δ = 60°

Папороть у стилі Барнслі

Аксіома: X
Правила: X → F+[[X]-X]-F[-FX]+X, F → FF
δ = 25°
Стохастичні L-системи

Справжні рослини не є ідеально самоподібними. Поширений трюк — стохастична 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 Серпінського, папороть Барнслі та інші — і налаштовуйте довжину кроку, кут та кількість поколінь наживо.

Відкрити симуляцію →

🔗 Пов'язані симуляції

🌳Fractal Tree 🌊Flow Fields 🌃Generative City 🌀Spirograph