Проект fract-ol школы 42 — один из трех графических проектов cursus для начинающих. Он научит вас работе с низкоуровневой графической библиотекой, продвинутой математике и многому другому.

Весь код из этого проекта можно найти в моем репозитории GitHub, не стесняйтесь посмотреть, не копируйте и не вставляйте этот код, не подумав, в следующей статье содержится достаточно объяснений и ресурсов, чтобы вы могли отправиться в свое собственное путешествие с ужасным МиниЛибХ ❤️
Как делать Фрак-ол?
Математика фракталов
Фракталы состоят из математических наборов.
Например, наборы Юлия и Мандельброт определяются следующим набором:

Все фракталы основаны на комплексных числах (то есть числах с действительной и мнимой частью, например z = a + bi).
К счастью, нам не нужно понимать всю математическую концепцию, стоящую за этим, чтобы закодировать fract-ol.
Здесь есть отличное видео (на французском 🇫🇷) от DIMENSION CODE, объясняющее концепцию фракталов и способы их создания на C++:
Видео длится ≈ 20 минут, но ту часть, с которой нам нужно начать, можно резюмировать так:
- a (действительная) часть комплексного числа может быть представлена на оси X.
- b (мнимая) часть комплексного числа может быть представлена на оси Y.
- Это означает, что координаты (3, 7) представляют собой числоz = 3 + 7i, и что каждый пиксель окна может использоваться для представления комплексного числа.
Каждое комплексное число, помещенное в набор, будет либо:
- сходится к конечному числу
- расходиться до бесконечности
Пиксели окна могут быть цветными в зависимости от того, сходится или расходится комплексное число, которое они представляют.
Если мы закрасим каждый пиксель окна черным, если комплексное число, которое они представляют, сходится, или белым, если оно расходится, мы увидим, что он уже генерирует фрактал:

От математики к коду
- Настройте библиотеку MiniLibX.
Вы можете найти полезные ресурсы о том, как это сделать здесь. - Создайте окно, изображение и все необходимое в MiniLibX.
- Перебрать каждый пиксель окна.
См.draw_fractal. - Для каждого пикселя подсчитайте комплексное число, которое он представляет, и поместите его в набор.
См., например,calculate_mandelbrot. - Если набор расходится, окрашиваем пиксель в белый.
Если набор сходится, окрашиваем пиксель в черный.
Основные функции
draw_fractal
int draw_fractal(t_fractal *fractal, char *query, double cx, double cy)
{
fractal->x = 0;
fractal->y = 0;
while (fractal->x < SIZE)
{
while (fractal->y < SIZE)
{
if (ft_strncmp(query, "mandel", 7) == 0)
calculate_mandelbrot(fractal);
else if (ft_strncmp(query, "julia", 6) == 0)
calculate_julia(fractal, cx, cy);
else if (ft_strncmp(query, "ship", 5) == 0)
calculate_burning_ship(fractal);
else
{
ft_putendl_fd("Available fractals: mandel, julia, ship", 1);
exit_fractal(fractal);
}
fractal->y++;
}
fractal->x++;
fractal->y = 0;
}
mlx_put_image_to_window(fractal->mlx, fractal->window, fractal->image, 0,
0);
return (0);
}
Как объяснялось ранее, эта функция просто перебирает пиксели окна и вызывает соответствующую функцию для рисования фрактала.
calculate_mandelbrot
void calculate_mandelbrot(t_fractal *fractal)
{
int i;
double x_temp;
fractal->name = "mandel";
i = 0;
fractal->zx = 0.0;
fractal->zy = 0.0;
fractal->cx = (fractal->x / fractal->zoom) + fractal->offset_x;
fractal->cy = (fractal->y / fractal->zoom) + fractal->offset_y;
while (++i < fractal->max_iterations)
{
x_temp = fractal->zx * fractal->zx - fractal->zy * fractal->zy
+ fractal->cx;
fractal->zy = 2. * fractal->zx * fractal->zy + fractal->cy;
fractal->zx = x_temp;
if (fractal->zx * fractal->zx + fractal->zy
* fractal->zy >= __DBL_MAX__)
break ;
}
if (i == fractal->max_iterations)
put_color_to_pixel(fractal, fractal->x, fractal->y, 0x000000);
else
put_color_to_pixel(fractal, fractal->x, fractal->y, (fractal->color
* i));
}
Давайте проанализируем эту функцию шаг за шагом:
- Для переменных z установлено значение 0, начало набора.
Переменная c для констант устанавливаются координаты пикселя.
Из соображений производительности мы используем вычисление(x * x)вместо функцииpow(x, 2). - Набор повторяется до тех пор, пока:
• Абсолютное значение z больше, чем максимальные значения системы: набор будет расходиться до бесконечности.
• Количество количество итераций слишком велико: набор застрянет в бесконечном цикле. - Если набор расходится, мы раскрашиваем его и умножаем цвет на количество итераций, чтобы сделать математические глубины более понятными для глаза.
🎉 Забавный факт: цвет субтитров британского стандарта #FCBE11 дает довольно крутые психоделические рендеры, если умножить их на количество итераций*.
calculate_juliaиcalculate_burning_shipочень похожи наcalculate_mandelbrot, но с другими уравнениями.
calculate_julia
void calculate_julia(t_fractal *fractal, double cx, double cy)
{
int i;
double tmp;
fractal->name = "julia";
fractal->cx = cx;
fractal->cy = cy;
fractal->zx = fractal->x / fractal->zoom + fractal->offset_x;
fractal->zy = fractal->y / fractal->zoom + fractal->offset_y;
i = 0;
while (++i < fractal->max_iterations)
{
tmp = fractal->zx;
fractal->zx = fractal->zx * fractal->zx - fractal->zy * fractal->zy
+ fractal->cx;
fractal->zy = 2 * fractal->zy * tmp + fractal->cy;
if (fractal->zx * fractal->zx + fractal->zy
* fractal->zy >= __DBL_MAX__)
break ;
}
if (i == fractal->max_iterations)
put_color_to_pixel(fractal, fractal->x, fractal->y, 0x000000);
else
put_color_to_pixel(fractal, fractal->x, fractal->y, (fractal->color * (i
% 255)));
}
Функция calculate_julia имеет точно такую же структуру, что и функция calculate_mandelbrot, с той лишь небольшой разницей, что переменные z задаются координатами пикселя, а константы c предоставляются с самого начала.
Это позволяет Джулии формировать разные формы при изменении констант c, в то время как Мандельбро всегда делает то же самое.
calculate_burning_ship
void calculate_burning_ship(t_fractal *fractal)
{
int i;
double x_temp;
fractal->name = "ship";
i = 0;
fractal->zx = 0.0;
fractal->zy = 0.0;
fractal->cx = (fractal->x / fractal->zoom) + fractal->offset_x;
fractal->cy = (fractal->y / fractal->zoom) + fractal->offset_y;
while (++i < fractal->max_iterations)
{
x_temp = fractal->zx * fractal->zx - fractal->zy * fractal->zy
+ fractal->cx;
fractal->zy = fabs(2.0 * fractal->zx * fractal->zy) + fractal->cy;
fractal->zx = fabs(x_temp);
if (fractal->zx * fractal->zx + fractal->zy
* fractal->zy >= __DBL_MAX__)
break ;
}
if (i == fractal->max_iterations)
put_color_to_pixel(fractal, fractal->x, fractal->y, 0x000000);
else
put_color_to_pixel(fractal, fractal->x, fractal->y, (fractal->color
* i));
}
И снова calculate_burning_ship имеет идентичную структуру, за исключением уравнения, на этот раз zₙ₊₁ = abs(zₙ)² + c.
Демонстрации
Мандельброт


Юлия


Горящий корабль


Ресурсы и кредиты
Понимание концепции фракталов и реализация программы были сделаны с помощью следующих ресурсов.
Математические принципы, уравнения и преобразование в код
Использование потоков для повышения производительности (на данный момент сюда не включено)
Нарисовать пиксели на изображении
💡 Не используйте
mlx_pixel_put, как я сначала.
Это медленно, и вы не можете повторно визуализировать холст.
Вместо этого используйте
mlx_put_image_to_window.