Алгоритм позволяет генерировать изображения, похожие на “инопланетную фауну” или абстрактные изображения.
Изображения создаётся на основе таблицы, которая символизирует ДНК.
Каждый раз из одной таблицы (ДНК) получаются немного разные изображения.
Можно генерировать случайные ДНК, скрещивать ДНК разных растений,
делать мутацию случайного гена в ДНК и в итоге пользователь получает ДНК, которое синтезирует красивые изображения.
Геном
Геном представляет из себя таблицу размером 14 на 26,
Заполненную числами от 0 до 255.
По строкам идут признаки растения (цвет, размер, угол и тд)
По колонкам уровни (что то вроде: ствол, ветки, листья ..)
Первых две строки являются служебными ( INFO и START).
способ рисования - Агенты
Рисование растения производится с помощью «агентов».
Агент использует данные из колонки таблицы, в общем случае, соответствующего его уровню.
В начале рисования создаётся первый агент (уровень 0), он берёт данные, необходимые для работы, из первой колонки таблицы (колонка № 0).
Также он получает начальные данные для расчёта размера и цвета из строки START.
Из этих данных он вычисляет свой размер (диаметр кружка, рисуемого агентом) цвет, направление движения, должны ли меняться со временем размер, цвети, направление, длина рисуемого сегмента ( как долго агент будет существовать ), количество ответвлений ( сколько агентов он породит в конце своего цикла ).
Каждый шаг, агент рисует окружность заданного размера и цвета и смещается на три пикселя.
В конце каждого шага, если это необходимо, производит изменения в цвете, размерах и в направлении.
Когда агент сделает необходимое количество шагов, он исчезает,
при этом вместо себя он создаёт от одного до трёх новых агентов
(это прописанно в соответствующей ячейке таблицы, в строке CHILD_COUNT ) и передаёт им свой цвет, координаты и размер.
Также задаёт им направление.
Новые агенты ( уровень 1 ) берут данные уже из следующей колонки таблицы (колонка № 1) и на основе этих данных и данных, полученных от предшественника, вычисляют всю необходимую информацию для своей работы.
По окончанию своей работу они создают агентов третьего уровня…
Так продолжается до последнего уровня (всего 10 уровней, от 0 до 9)
и процесс генерации изображения завершается.
Обычно агент использует значения из колонки, соответствующий своему уровню, но иногда возникает ситуация, когда агент использует другую колонку, об этом будет описано ниже.
варианты отрисовки окружностей
При создании плоского изображения сначала рисуется круг большего размера и поверх него круг с меньшей прозрачностью и с размером, соответствующим тому, что должен нарисовать агент.
Благодаря этому, линии получаются более мягкие и отдельные круги сливаются в единую линию
При создании псевдо 3d изображения сначало рисуются чёрный и белый полупрозрачные круги со смещением по диагонали и поверх них круг нужного цвета. Это придаёт некоторую объёмность.
--------------
Описание строк таблицы (признаки)
--------------
_INFO - общая информация
В первые 3 байта INFO записаны размер таблицы и количество отображаемых уровней (26, 14, 10). Нужнo на случай добавления новых генов или уровней, что бы со старыми сохранёнками можно было работать.
Следующие 8 байтов - это номера родителей (по 4 байта на номер)
------
_START - информация от “предка”.
Каждый уровень использует информацию из предыдущего уровня.
В строке _START записана информация для самого первого уровня.
В первой ячейке размер, в трех последующих - значение составляющих цвета (R, G, B).
---------
Вычисление цвета
---------
_RED, _GREEN, _BLUE - красная зелёная и синяя состовляющие цвета.
Эти данные будут использоваться при вычислении цвета агента.
------
_COLOR_FROM_PARENT - значение, определяющие, в каких пропорциях,
для вычисления цвета агента, использовать цвет из ячеек _RED, _GREEN, _BLUE и цвет, переданный предшественником.
Если _COLOR_FROM_PARENT в диапазоне (0 .. 100) то пропорционально вычисляется промежуточный цвет между собственным цветом (_RED, _GREEN, _BLUE) и цветом предка.
Расчёт производится отдельно по каждой компоненте цвета (R, G, B)
Если _COLOR_FROM_PARENT в диапазоне (101 .. 255), то используется цвет предка.
_COLOR_VARIABLE - добавляем небольшую разницу в цвете
- если значение в диапазоне (0 .. 49) - цвет остаётся без изменений
- если значение в диапазоне (50 .. 255) - вычисляем измененение в цвете.
Сначала вычисляется временная переменная var, которая пропорциональна значению ячейки (50 ... 255) и принимает значение от 0 до 25.
Затем к каждой составляющей цвета (R, G, B) добавляется случайное число из диапазона от -var до var
_RED_CHANGE, _GREEN_CHANGE, _BLUE_CHANGE - значения, отвечающие за необходимость менять цвет на каждом шаге (градиент).
Каждый ячейка контролирует свою составляющую цвета (RGB)
- если значение в диапазоне (0 .. 119) - добавка к цвету будет в диапазоне (-8 .. 0)
- если значение в диапазоне (120 .. 135) - добавка равна 0
если значение в диапазоне (136 .. 255) - добавка к цвету будет в диапазоне (0 .. 8)
-------
Вычисление размера рисуемых окружностей
-------
Размер рисуемой агентом окружности определяется несколькими параметрами:
- собственным размером, заданным геномом
- размером предка
- уровнем
-----
_SIZE - значение этой ячейки задаёт собственный размер агента.
_SIZE_FROM_PARENT - определяет, в каких пропорциях, при вычислении размера, использовать информацию из ячейки _SIZE и из размера предка.
_SIZE_OR_LEVEL - это значение показывает, насколько нужно при вычислении размера учитывать уровень, который рисует агент.
(Чем более высокий уровень, тем с большей вероятностью, тоньше ветки)
Сначала вычислим две временных переменных
- temp_size1- размер зависит от генома и размера предка
- temp_size2- размер зависит отtemp_size1и уровня
temp_size1
- Если _SIZE_FROM_PARENT в диапазоне ( 0 .. 49 ), то temp_size1 равен _SIZE / 6
- Если _SIZE_FROM_PARENT в диапазоне ( 50 .. 200 ), то temp_size1 пропорционально в промежутке от ( _SIZE / 6 ) до размера предка.
- Если _SIZE_FROM_PARENT в диапазоне ( 201 .. 255 ), то temp_size1 равен размеру предка
temp_size2
Это переменная зависит от уровня, который рисует агент, чем выше, тем тоньше
temp_size2 = 30 - (3 * уровень)
То есть размер начального 0 уровня = 30, размер последнего 9 уровня = 3
конечный подсчёт
- Если _SIZE_OR_LEVEL в диапазоне ( 0 .. 19 ), то размер окружности равен temp_size1
- Если _SIZE_OR_LEVEL в диапазоне ( 20 .. 100 ), то то размер окружности будет в промежутке от temp_size1 до temp_size2
- Если _SIZE_OR_LEVEL в диапазоне ( 101 .. 255 ), то размер окружности равен temp_size2
Здесь есть перекос в сторону размера, зависимого от уровня.
Эксперименты показали, что при этом чаще получаются более красивые результаты.
Вариативность
Добавим разнообразия.
_SIZE_VARIABLE делает размер чуть больше или меньше.
Если значение ячейки в диапазоне ( 200 .. 255 ), то размер меняется на случайную величину.
Размах возможного изменения пропорционален значениею ячейки и может измениться в диапазоне от 0 до размер/3 в любую сторону.
Изменение размера по ходу роста
_SIZE_CHANGE - определяет, изменяется ли размер рисуемых агентом окружностей на следующем шаге.
- Если значение в диапозоне (0 .. 80), то размер рисуемых окружностей каждый ход уменьшается на 0 .. 0.5 пиксель.
- Если значение в диапозоне (81 .. 174), то размер рисуемых окружностей не меняется.
- Если значение в диапозоне (175 .. 255), то размер рисуемых окружностей каждый ход увеличивается на 0 .. 0.5 пиксель
Размер окружности, которую рисует агент может уменьшится до 0 и стать отрицательным.
Окружность с отрицательным диаметром не отличается от окружности с положительным диаметром.
При рисовании, если размер окружности в диапазоне от -3 до 3, то рисуется с размером 3 пикселя.
-------
Вычисление длины сегмента
-------
_LENGTH - определяет количество шагов, которые сделает агент, тем самым определяет длину ветки. Значение ячейки делится на 5, таким образом ветка может быть нарисована с использованием от 0 до 51 окружностей.
_LENGTH_OR_LEVEL - определяет, нужно ли учитывать уровень, который рисует агент
- если значение в диапазоне (0 .. 199), то длина сегмента определяется геном _LENGTH
- если значение в диапазоне (200 .. 255), то длина сегмента плавно меняется от длины, определённой геном _LENGTH, до длины, определённой списком: (100, 70, 50, 40, 32, 26, 20, 15, 10, 5)
_LENGTH_VARIABLE - определяет размер добавки, которая изменяет количество шагов агента, тем самым обеспечивая разный размер веток.
- Если значение ячейки в диапазоне (0 .. 49), то размер не меняется.
- Если значение ячейки в диапазоне (50 .. 255), то количество шагов меняется в случайную сторону на случайное значение из рассчитанного диапазона от нуля, до длина/3
-------
Направление роста сегмента
-------
_ANGLE_CHANGE - определяет постоянное изменение направления агента. Благодаря этому гену, ветка загибается по кругу.
- Если значение в диапазоне (0 .. 40), то каждый ход агент поворачивает на небольшой угол. Чем значение ближе к 0, тем больше этот угол (до 0.1 радиан). Центральный отросток может поворачивать в случайную сторону, а боковые отростки поворачивают наружу.
- Если значение в диапазоне (215 .. 255), то каждый ход агент поворачивает на небольшой угол, но уже в другом направлении. Чем ближе к 255, тем больше этот угол (до 0.1 радиан). Центральный отросток может поворачивать в случайную сторону, а боковые отростки поворачивают внутрь
- Если значение в диапазоне (41 .. 214), то агент не изменяет направление.
_ANGLE_ACCELER - ускорение поворота.
Каждый ход немного изменяет постояную добавку к углу поворота, которая была получена из _ANGLE_CHANGE, тем самым агент начинает двигаться по спирали.
- Если значение в диапазоне (0 .. 20), то каждый ход добавка увеличивается на небольшое значение. Чем ближе значение к 0, тем больше прибавка. (до 0.0125 радиан). Центральный отросток может поворачивать в случайную сторону, а боковые отростки поворачивают наружу
- Если ген в диапазоне (235 .. 255), то каждый ход добавка уменьшается на небольшое значение. Чем ближе значение к 255, тем на большее значение происходит изменение. (до 0.0125 радиан). Центральный отросток может поворачивать в случайную сторону, а боковые отростки поворачивают внутрь
- Если значение в диапазоне (21 .. 234), то изменение направления агента остаётся постоянным.
_ANGLE_RANDOM - случайные повороты во время роста, благодаря этому появляются извивающиеся ветки.
- Если значение в диапазоне (0 .. 127), то никаких случайных поворотов не происходит
- Если значение гена в диапазоне (128 .. 255), то каждый ход направление агента немного меняется на небольшой случайный угол из диапазона, который меняется от (0 .. 0) до (-0.5 .. +0.5) пропорционально значению гена.
вычисление производится каждый ход
_UP_DOWN - этот ген определяющий загиб веток вверх или вниз.
Изменение направления происходит каждый ход на небольшую величину, которая зависит от гена и толщины ветки.
Для вычисления используется абсолютное значение размера агента, но не меньше 1
- Если ген в диапазоне (0 .. 54), то ветки начинают загибаться вниз.
Изменение угла агента в радианах вычисляется по формуле:
( ( 55 - ген ) / 100 ) / размер агента
- Если ген в диапазоне (55 .. 200), то направление не меняетсяЕсли ген в диапазоне (201 .. 255), то агент каждый ход немного поворачивается к направлению вверх.
Изменение угла агента в радианах вычисляется по формуле:
( ( ген - 200) / 10000 ) * размер агента
Из за того, что в формуле участвует размер агента, при одном и том же значении гена тонкие ветки быстрее загибаются вниз, а толстые вверх.
---------
Направление ответвлений
---------
Когда Агент сделал все шаги и он не на последнем уровене, то он создаёт от 1 до 3 ответвлений.
_ANGLE_CHILD - определяет угол, под которым будут расти ответвления.
Значение ячейки делится на 160 и получается угол в радианах.
Если ответвлений два:
- направление первого ответвления - это направление предка минус полученное значение
- направление второго ответвления - это направление предка плюс полученное значение.
Тоже самое и для боковых ответвлений, если ответвлений три.
Если ответвление одно или для центрального при трёх ответвлениях
используется направление потомка.
_ANGLE_CHILD_VARIABLE - добавляет небольшое изменение для начального направления у потомков.
- Если значение гена в диапазоне (0 .. 49), то направление у потомков не меняется.
- Если значение гена в диапазоне (50 .. 255), то к углу роста потомка добавляется случайное число из диапазона, который меняется от (0 .. 0) до (-0.8 .. +0.8) пропорционально значению гена.
---------
Количество ответвлений
--------
_CHAILD_COUNT - показывает, сколько ответвлений будет у агента, после того, как его жизненный цикл закончится.
- Если значение в диапазоне (0 .. 49), то будет создано одно ответвление.
- Если значение в диапазоне (50 .. 99), то будет от 1 до 2 ответвлений, чем ближе к 100, тем вероятнее 2 ответвления.
- Если значение в диапазоне (100 .. 149), то будет 2 ответвления
- Если значение в диапазоне (150 .. 179), то будет от 2 до 3 ответвлений, чем ближе к 179, тем вероятнее 3 ответвления.
- Если значение в диапазоне (180 .. 231), то будет 3 ответвления
- Если значение в диапазоне (232 .. 255), то будет 3 ответвления, причём центральное ответвление будет стандартным и будет использовать следующую колонку в геноме и считаться уровнем на 1 уровень выше, чем предшественник.
Два боковых ответвления будут иметь уровень и использовать колонку из генома, которые вычисляется по формулам:
уровень новой ветки = уровень + 1 + _CHAILD_COUNT % 4
колонка, используемая новой веткой = _NEXT_GEN % 14
В любом случае уровень нового ответвления будет выше, что бы избежать рекурсии
_NEXT_GEN - значение, с помощью которого определяется, какую колонку будут использовать боковые ветки в последнем варианте разветвления.
Колонка для боковых ответвлений вычисляется по формуле:
_NEXT_GEN % 14 ( на количество колонок в геноме)
пример
Например, последняя отрисованная ветка имеет уровень 4 и использует 4колонку в геноме.
Смотрим колонку номер 4.
_CHAILD_COUNT = 253
_NEXT_GEN = 1
Значит будет три ответвления.
Боковые ветки получат уровень равный шести
4 + 1 + (253 % 4) = 6
и будут использовать данные из первой колонки
1 % 14 = 1
то есть:
Центральное ответвление получит 4+1=5 уровень
и будет использовать 4+1=5 колонку.
Боковые ответвления будут иметь 6 уровень
и использовать 1 колонку