Среди нелинейных фракталов один выделяется особняком — это фрактал, который называется фракталом Мандельброта и представляет собой отображение некоторого множества комплексных точек на плоскости.
Алгоритм построения множества таких точек достаточно прост: берется простейшее итерационное уравнение в комплексных числах с возведением некоторого стартового числа в квадрат и прибавлением константы. Но поскольку, это целое множество точек, каждый раз в ходе итераций по уравнению, константа изменяется в интервале [-2.0 + 2.0i; 2.0 + 2.0i] — и каждый раз проверяется не уходит ли точка из прямоугольной области 2 * 2: для чего проверяется, меньше ли модуль полученного значения чем 2 (если стартовое значение после некоторого количества итераций дает модуль больше 2, то значит точка с соответствующими координатами не принадлежит множеству Мандельброта и скорее всего траектория этой точки уйдет в бесконечность).
Собственно, наша задача проста — необходимо провести итерации по вот такому уравнению:
zn = (z0 * z0) + c; где zn - результат очередной итерации, z0 - начальная точка (0.0 + 0.0i), с - константа
и отобразить некоторые точки.
В итоге, получается:
import dgui.all; import std.complex; // отрисовка отдельных точек void drawPoint(Canvas c, int x, int y) { Pen p = new Pen(Color(0, 0, 0), 1, PenStyle.solid); c.drawLine(p, x, y, x + 1, y + 1); }; void drawMandelbrotSet(Canvas canv, float step) { for (float i = -2.0; i < 2.0; i += step) { for (float j = -2.0; j < 2.0; j += step) { bool isMandelbrotPoint = true; auto c = complex(i,j); auto z0 = complex(0.0, 0.0); for (ubyte k = 0; k < 255; k++) { auto zn = (z0 * z0) + c; if (abs(zn) > 2) { isMandelbrotPoint = false; break; } z0 = zn; } if (isMandelbrotPoint) { auto X = cast(int) (150 - 200 * i); auto Y = cast(int) (250 - 200 * j); drawPoint(canv, X, Y); } } } } class MainForm : Form { public this() { this.text = ""; this.size = Size(500, 550); this.startPosition = FormStartPosition.centerScreen; }; protected override void onPaint(PaintEventArgs e) { Canvas c = e.canvas; drawMandelbrotSet(c, 0.005); super.onPaint(e); } }; int main(string[] args) { return Application.run(new MainForm()); }
Как оно работает: создаем приложение DGui, после чего перегружаем, как обычно, функцию onPaint, подставляя в нее вызов drawMandelbrotSet(c, 0.005), где 0.005 — это приращение для каждого из циклов, кроме цикла с итерациями по самой формуле. Переменная isMandelbrotPoint обозначает принадлежность точки к множеству Мандельброта и установлена в начале в true, так как предполагается изначально, что точка принадлежит множеству, а значение этой переменной изменяется лишь в том случае, если в самом внутреннем цикле модуль комплексного числа превысит число 2 (т.е. в том случае, если точка выйдет в бесконечность).
Одним из не очевидных моментов может быть подключение std.complex, части стандартной библиотеки, которая содержит структуру complex и функцию abs (модуль комплексного числа). Дело в том, что стандартными средствами D, несмотря на нативную поддержку комплексных чисел, нельзя построить комплексное число, используя в качестве аргументов значения действительной и мнимой части, кроме того, эта библиотека содержит еще и уйму полезных предопределенных функций и перегруженных операторов (например, сложение и умножение).
Ну и в конце концов, после компиляции получаем вот такую безрадостную картинку, чем-то напоминающую филейную часть тела: