Папоротник Барнсли
Папоротник Барнсли
Рассматривается реализация известного фрактального алгоритма "Папоротник Барнсли", который генерирует изображение папоротника с помощью системы итерируемых функций.
Введение
Алгоритм "Папоротник Барнсли" — это пример использования системы итерируемых функций (Iterated Function System, IFS) для генерации фракталов. Он был предложен математиком Майклом Барнсли и позволяет создать реалистичное изображение листа папоротника, применяя простые аффинные преобразования с определённой вероятностью.
Фрактал строится путём многократного применения случайных аффинных преобразований к точке на плоскости. Каждое из четырёх преобразований соответствует определённой части растения (стебель, левая ветвь, правая ветвь и верхушка), а вероятности выбора этих преобразований задают характерный вид папоротника.
Этот алгоритм широко используется как учебный пример в теории фракталов и компьютерной графике благодаря своей наглядности и математической красоте.
# Установка необходимого пакета
import Pkg; Pkg.add("Images")
using Images
Основная часть
Определение структуры BarnsleyFern
mutable struct
определяет изменяемую структуру данных, в которой будут храниться все параметры и текущее состояние нашего фрактала
mutable struct BarnsleyFern
# размер изображения по ширине и высоте в пикселях
width::Int
height::Int
# цвет, которым будет рисоваться папоротник
color::RGB
# текущие координаты точки x и y
x::Float64
y::Float64
# двумерный массив (матрица) пикселей изображения
fern::Matrix{RGB}
# Конструктор структуры. Вызывается при создании нового объекта BarnsleyFern()
function BarnsleyFern(width, height, color = RGB(0.0, 1.0, 0.0), bgcolor = RGB(0.0, 0.0, 0.0))
# Создаём пустое изображение размером width на height,
# заполняя его фоновым цветом bgcolor
img = [bgcolor for x in 1:width, y in 1:height]
# Вычисляем начальные координаты центральной точки в пикселях:
# cx вычисляется с учётом диапазона значений x от -2.182 до 2.6558 (примерно 4.8378)
cx = Int(floor(2.182 * (width - 1) / 4.8378) + 1)
# cy вычисляется с учётом диапазона значений y от 0 до 9.9983
cy = Int(floor(9.9983 * (height - 1) / 9.9983) + 1)
# Устанавливаем первый пиксель в начальной точке
img[cx, cy] = color
# Создаём новый экземпляр структуры BarnsleyFern
# с указанными параметрами и инициализированной матрицей изображения
return new(width, height, color, 0.0, 0.0, img)
end
end
Структура BarnsleyFern
содержит информацию о размерах изображения, текущем положении точки, цвете отрисовки и самой матрице пикселей. Конструктор создаёт чёрный фоновый прямоугольник и устанавливает начальную точку роста папоротника в центральном нижнем участке области отрисовки.
Определение трансформации точки
Функция, которая выполняет одну итерацию алгоритма: выбирает одно из четырех аффинных преобразований и применяет его к текущим координатам точки (f.x, f.y)
, после чего переводит новые вещественные координаты в дискретные пиксельные и закрашивает соответствующий пиксель на изображении.
function transform(f::BarnsleyFern)
# Генерируем случайное число от 0 до 99 (включительно)
r = rand(0:99)
# В зависимости от значения r выбирается одно из 4 аффинных преобразований:
# Вероятности подобраны так, чтобы получился реалистичный вид папоротника:
# 1% — вертикальное сжатие (ствол)
# 85% — крупные листья сверху (главное преобразование)
# 7% — маленькие листья слева
# 7% — маленькие листья справа
if r < 1
# Превращение точки в корневую часть ствола
f.x = 0.0
f.y = 0.16 * f.y
elseif r < 86
# Главное преобразование: формирование основной массы листвы
f.x = 0.85 * f.x + 0.04 * f.y
f.y = -0.04 * f.x + 0.85 * f.y + 1.6
elseif r < 93
# Формирование левой стороны листвы
f.x = 0.2 * f.x - 0.26 * f.y
f.y = 0.23 * f.x + 0.22 * f.y + 1.6
else
# Формирование правой стороны листвы
f.x = -0.15 * f.x + 0.28 * f.y
f.y = 0.26 * f.x + 0.24 * f.y + 0.44
end
#########################################################################
# Переход от декартовых координат к индексам матрицы изображения
# Так как область значений (x ∈ [-2.182, 2.6558], y ∈ [0, 9.9983])
# нужно правильно смасштабировать координаты и перевести их в номера пикселей
# Для координаты x:
# минимальное значение x=-2.182 должно соответствовать первому столбцу (индекс 1)
# максимальное значение x=2.6558 → ширина-1
cx = Int(floor((f.x + 2.182) * (f.width - 1) / 4.8378) + 1)
# Для координаты y:
# в системе координат экрана ось Y направлена вниз, поэтому необходимо
# выполнить преобразование координат (отражение относительно горизонтальной оси)
# y=0 → последняя строка height
# y=9.9983 → первая строка 1
cy = Int(floor((9.9983 - f.y) * (f.height - 1) / 9.9983) + 1)
# Проверяем границы изображения перед установкой пикселя
if 1 <= cx <= f.width && 1 <= cy <= f.height
# Устанавливаем пиксель по новым координатам в заданный цвет
f.fern[cx, cy] = f.color
end
end
Функция transform()
моделирует случайный процесс IFS. На каждом шаге она случайным образом выбирает одно из четырех аффинных преобразований, изменяет координаты текущей точки и записывает эту точку на изображении.
Чтобы сохранить пропорции при переходе от области определения функций () к матрице пикселей, используются масштабирующие множители и сдвиги:
- Для X:
cx = floor((x + 2.182) × (ширина − 1) ÷ 4.8378)
- Для Y:
cy = floor((9.9983 − y) × (высота − 1) ÷ 9.9983)
, где происходит зеркальное отображение
Выполнение итераций и вывод результата
Создаем экземпляр нашего фрактала размером 500×500 пикселей
const fern = BarnsleyFern(500, 500)
Запускаем миллион итераций – каждая шаг применяет одно из аффинных преобразований и добавляет точку в соответствующий пиксель изображения
for _ in 1:1000000
transform(fern)
end
Передаем обратно результат как транспонированную матрицу пикселей. Это связано с различием ориентации осей в экранной и математической системах координат
fern.fern'
Данный блок кода запускает генерацию фрактального изображения. Проходит миллион шагов итераций, затем мы обращаемся к содержимому поля .fern
и транспонируем матрицу для корректного отображения в графических библиотеках.
Заключение
Мы подробно разобрали реализацию алгоритма «Папоротник Барнсли» на языке программирования Julia. Он демонстрирует применение системы итерируемых функций (IFS) и позволяет наглядно увидеть фрактальную природу самоподобных объектов.
Код создает цветное изображение в виде матрицы RGB-значений, которое может быть в дальнейшем использовано или визуализировано различными способами.
Такой подход полезен в образовательных целях, для понимания принципов работы фракталов, а также в практическом программировании при работе с рекурсивными графическими объектами и моделировании природных структур.
Пример разработан с использованием материалов Rosetta Code