Давайте начнём с азов, если брать определение из всем известной и всеми любимой Википедии, то L-система (или же система Линденмайера) — это параллельная система переписывания и вид формальной грамматики.

Если говорить простым языком, то L-система состоит из алфавита символов, которые могут быть использованы для создания строк, набора порождающих правил, которые задают правила подстановки вместо каждого символа, начальной строки ( “аксиомы” ), с которой начинается построение, и механизм перевода образованной строки в геометрические структуры. Самым простым примером L-системы может служить задача построения дерева.

Вводные данные:

Строка (далее Аксиома): A B

Переменные (которые мы можем задействовать в построении дерева): A B C

Правило (правило по которому каждая переменная на последующие строке меняется):

  • A - > AB

  • B - > AC

  • C - > A

Получаются такие преобразования:

Поколение

Состояние

1

A B

2

AB AC

3

AB AC AB A

4

AB AC AB A AB AC AB

5

AB AC AB A AB AC AB AB AC AB A AB AC

6

и так далее…

Основным направлением, в котором применяются L-системы, это моделирование процессов роста как живых организмов, так и неживых объектов (кристаллов, раковин моллюсков или пчелиных сот).

Пример:

Для моделирования подобных процессов, мы с вами будем использовать такой язык программирования как Python, в нём есть встроенная библиотека “turtle”.

Итак, приступим:

  1. Здесь мы импортируем библиотеку Turtle в наш проект:

    import turtle

  2. Далее мы подключаем все необходимые конфигурации для нашей черепашки:

    turtle.hideturtle()

    turtle.tracer(1)

    turtle.penup()

    turtle.setposition(-300,340)

    turtle.pendown()

    turtle.pensize(1)

  3. Далее мы задаём значение непосредственно самой аксиомы и сопутствующих параметров, необходимых для задания последовательности:

    axiom = "F+F+F+F"

    tempAx = ""

    itr = 3

    (itr- значения итераций цикла, оно нам понадобится в следующем шаге при написании нашей программы)

  4. В данном цикле, где как раз нам и понадобится переменная itr-, мы занимаемся обработкой и "выращиванием" непосредственно генома нашего фрактала/растения:

    for k in range(itr):

    for ch in axiom:

    if ch == "+":

    tempAx = tempAx + "+"

    elif ch == "-":

    tempAx = tempAx + "-"

    elif ch == "F": #F

    tempAx = tempAx + "F+F-f-F+F"

    else:

    tempAx = tempAx + "f"

    axiom = tempAx

    tempAx = " "

    print(axiom)

    Если мы с вами пробежимся по циклу, то сразу же в первом условии мы увидим фильтр на символ "+":

    if ch == "+":

    tempAx = tempAx + "+"

    Здесь мы ищем в аксиоме (изначальной строке) знак “+” и при его появлении мы добавляем символ “+” в последующую строчку. Так же происходит и с символом “-” и “f”, мы просто добавляем в последующую строчку символы “-” и “f” соответственно. Но при появлении в нашей аксиоме символа “F”, мы поступим немного иначе и добавим в последующую строчку уже последовательность символов “F + F – f – F + F”, для увеличения длины каждой последующей строки. Данное действие в принципе не принципиально, но для быстроты генерации “генома”, я решил поступить именно так. В конце мы обязательно приравниваем аксиому к нашему геному и обнуляем его:

    axiom = tempAx

    tempAx = " "

    Результат (значение аксиомы):

  5. Ну что, вроде по генерации генома всё понятно, давайте уже приступим непосредственно к визуализации. Для этого нам понадобиться простенький цикл, который будет переводить встречающиеся в геноме символы в движения черепашки:

    for ch in axiom:

    if ch == "+":

    turtle.right(45)

    turtle.forward(10)

    turtle.right(45)

    elif ch == "-":

    turtle.left(45)

    turtle.forward(10)

    turtle.left(45)

    else:

    turtle.forward(20)

    Сразу можно заметить, что по аналогии с предыдущим пунктом, мы детектируем символы "+", "-", "F" и "f". Только теперь в момент, когда мы встречаем символ "+":

    if ch == "+":

    turtle.right(45)

    turtle.forward(10)

    turtle.right(45)

    Мы поворачиваем сначала на право, на 45 градусов, потом проезжаем расстояние, равное 10, и потом мы снова поворачиваем на право, на 45 градусов. Когда же мы встречаем символ "-":

    elif ch == "-":

    turtle.left(45)

    turtle.forward(10)

    turtle.left(45)

    Мы делаем все те же самые действия, что и при символе "+", только на этот раз поворачиваем уже не вправо, а уже влево. Но если же мы встретим символы "F" или "f", то мы просто буем проезжать вперёд на 20 пикселей:

    else:

    turtle.forward(20)

    В итоге мы получаем вот такой фрактал-снежинку:

  6. Если же мы захотим раскрасить наш фрактал-снежинку, то нам понадобиться перед циклом из предыдущего пункта добавить строчки:

    turtle.fillcolor("#99BBFF")

    turtle.begin_fill()

    Где #99BBFF - это кодировка цвета (RGB: 153, 187, 255), а begin_fill() это начало заполнения цветом. И в конце, уже после цикла, добавить строчки:

    turtle.end_fill()

    turtle.update()

    turtle.done()

    А end_fill() означает конец заполнения. Далее мы обновляем и выключаем нашу "черепашку". И на выходе мы получаем вот такой фрактал-снежинку:

Вы так же можете посмотреть, "потыкать" код, изменяя параметры и данные в нём.

В конце, хочу также добавить, что мне очень понравилось работать и писать по данной тематике, возможно, в будущем, я напишу ещё ряд статей по теме "L-системы", но а пока, хочу представить вам результаты моего тыканья творчества: