Gramatyka dla wzrostu
W 1968 roku botanik Aristid Lindenmayer opracował formalizm oparty na algorytmach strunowy (L-system) do modelowania podziałów komórek alg. L-system to formalna gramatyka G = (V, ω, P): alfabet V symboli, aksjomaty ω (początkowa ciąg znaków), oraz zbiór reguł produkcji P mapujący każdą zmienną na ciąg zastępczy. Co odróżnia L-systemy od standardowej gramatyki Chomsky'ego jest to, że każdy symbol w ciągu jest jednocześnie przepisywany na każdym etapie generowania – równoległe zastępowanie, a nie sekwencyjne - co właśnie wytwarza samopodobną strukturę rozgałęziającą się.
Algae (Lindenmayer's original 1968 example): Variables: A B Axiom: A Rules: A → AB, B → A Gen 0: A Gen 1: AB Gen 2: ABA Gen 3: ABAAB Gen 4: ABAABABA // length grows as Fibonacci numbers: 1,2,3,5,8,13…
Grafika żółwiańska: nadawanie ciągu znaków kształtem
Ciąg L-system staje się obrazem dopiero wtedy, gdy każdy znak jest interpretowany jako polecenie dla żółwi utrzymującej pozycję, kąt głowy i stos do rozgałęziania: F oznacza ruch do przodu rysując segment, +/− obrót w lewo/prawo o stały kąt δ, oraz [/] wciskanie i wyciskanie stanu żółwi. Para nawiasów tworzy gałąź: wcisnąć na podstawie, narysować odgałęzien i wrócić do dokładnie tego miejsca, gdzie zaczęła się gałąź.
Fractal Plant (δ=25°): axiom X, rules X → F+[[X]−X]−F[−FX]+X, F → FF Koch curve (δ=60°): axiom F, rule F → F+F−−F+F // D = log4/log3 ≈ 1.262 Sierpiński (δ=60°): F−G−G, F→F−G+F+G−F, G→GG // D = log3/log2 ≈ 1.585
JavaScript: rozszerzaj i rysuj
Silnik posiada dwie niezależne fazy: rozbudowa aksjomatu do n generacji, a następnie iteracja po wynikowej łańcuchu w Canvas 2D.
funkcja expand(aksjomat, zasady, geny) { let str = aksjomat; for (let g = 0; g < geny; g++) { str = str.replace(/([A-Za-z]+)/g, (match, ch) => rules[ch] ? rules[ch].split(' ').join('') : ch); } return str; } funkcja draw(ctx, str, x, y, kat, krok, delta) { const stos = []; ctx.beginPath(); ctx.moveTo(x, y); for (const ch of str) { if (ch === 'F') { x += krok*Math.cos(kat); y -= krok*Math.sin(kat); ctx.lineTo(x, y); } else if (ch === '+') kat += delta; else if (ch === '-') kat -= delta; else if (ch === '[') stos.push({ x, y, kat }); else if (ch === ']') ({ x, y, kat } = stos.pop()); } ctx.stroke(); } Zasada typu F → FF przybliżnie podwaja długość łańcucha co generację – 10. generacja już produkuje 1024 segmenty, a 20. generacja ponad milion. Po kilku generacjach znacznie taniej jest pominąć materializację pełnego łańcucha i interpretować gramatykę rekurencyjnie z licznikiem głębokości, produkując identyczne rysunek w stałej przestrzeni stosu.
function expand(axiom, rules, gens) {
let str = axiom;
for (let g = 0; g < gens; g++)
str = [...str].map(ch => rules[ch] ?? ch).join('');
return str;
}
function draw(ctx, str, x, y, angle, step, delta) {
const stack = [];
ctx.beginPath(); ctx.moveTo(x, y);
for (const ch of str) {
if (ch === 'F') { x += step*Math.cos(angle); y -= step*Math.sin(angle); ctx.lineTo(x, y); }
else if (ch === '+') angle += delta;
else if (ch === '-') angle -= delta;
else if (ch === '[') stack.push({ x, y, angle });
else if (ch === ']') ({ x, y, angle } = stack.pop());
}
ctx.stroke();
}
Losowe zasady i trójwymiarowe żółwie
Zdeterministiczną regułę można zastąpić kilkoma alternatywnymi wyrazami, każdemu przypisanym prawdopodobieństwo sumujące się do 1 — uruchomienie tej samej gramatyki dwa razy daje dwa różne rośliny, co jest niezbędne, aby uniknąć sztucznej jednorodności czysto deterministycznego systemu. W 3D żółwia posiada pełny obrót w lewo/w prawo/do góry zamiast pojedynczego kąta, a symbole & ^ \ / stosuje macierze obrotu wokół osi tego ramienia, pozwalając gałęzi się wyginać i przechylać w przestrzeni przed tym, że F zatwierdza segment w bufor Three.js LineSegments.
Poza zamkniętymi gramatykami
Książka Prusinkiewicia i Lindenmeyera z 1990 roku "The Algorithmic Beauty of Plants" rozszerza podstawowy model znacznie: systemy L-system parametryczne dołączają liczby do symboli (F(l,w)), dzięki czemu gałęzie mogą się zwężać w każdym pokoleniu; otwarte systemy L-system wymieniają informacje z symulowanym środowiskiem, pozwalając na symulację pola świetlnego wygłasać zacienione gałęzie i generować realistyczne unikanie cieniowania; a kontekstowo zależne reguły warunkują zamianę na sąsiadujące symbole, modelując interkomórkowe sygnalizowanie chemiczne, które inspirowało oryginalną biologię Lindenmeyera.
Frequently asked questions
Co sprawia, że przepisywanie w systemach L różni się od normalnej gramatyki?
Każdy symbol w ciągu jest jednocześnie zastępowany w każdej generacji, a nie jeden po drugim, jak w standardowej gramatyce Chomsky'ego. Ta równoległa rewizja jest tym, co tworzy naturalnie samo-powtarzające się, rozgałęziające struktury zamiast losowej sekwencji pochodzenia.
Jak symbole push [ i pop ] tworzą gałęzie?
[ zapisuje bieżącą pozycję żółwia i kierunek na stos; ] przywraca ostatnio zapisaną stan. Wszystko narysowane między dopasowanymi symbolami odbywa się na bocznej gałęzi, która dokładnie wraca do punktu początkowego po zakończeniu – fundamentalna zasada stojąca za każdym drzewem, krzewem i paprocią w systemach L.
Dlaczego reguły takie jak F → FF powodują eksplozję długości ciągu?
Ponieważ długość ciągu rośnie w przybliżeniu dwukrotnie z każdą generacją, więc generacja 10 już produkuje 1024 segmenty, a generacja 20 ponad milion. Systemy L o wysokiej generacji powinny pomijać materializację pełnego ciągu i zamiast tego używać rekurencyjnego interpretera z licznikiem głębokości, który daje ten sam rysunek w stałej przestrzeni stosu.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz the simulation i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.
▶ Otwórz symulację the simulation