Рисование фракталов

Окно программы с изображенным множеством Джулии.

Данная программа осуществляет построение и отрисовку трех наиболее известных «классических» фракталов –Kam Torus, множество Джулии и множество Мандельброта.

Фрактал – математическое понятие, обозначающее абстрактный образ, построенный по принципу самоподобия с помощью алгебраических уравнений.
Программа по заданным уравнениям рассчитывает значения, приводит их к форме, пригодной для отображения на экране в определенном цвете, и затем рисует по ним изображение. Чтобы построить по математическому выражению графическое изображение, программа рассчитывает много значений х и у, а затем в определяемых ими точках экрана ставит точки или рисует линии. Ниже подробно рассматривается построение всех трех фракталов.

Фрактал Kam Torus
Фрактал Kam Torus рисует последовательность торов -поверхностей 1-го порядка, имеющих в трехмерном пространстве форму бублика, однако в нашем двухмерном случае, представляющий собой подобие эллипса. График Kam Torus порождается наложением рядов точек в некоторой орбите, генерируемых набором уравнений, в которых переменная на каждом шаге увеличивается на единицу. Три уравнения, управляющие рисованием имеют следующий вид:
x(0) = y(0) = orbit / 3
x(n+1) = x(n)*cos(a) + (x2 - y(n))*sin(a)
y(n+1) = x(n)*sin(a) + (x2 - y(n))*cos(a)
После каждого прохода цикла значение orbit получает некоторое фиксированное приращение. Параметры, задающие поведение функции, включают в себя угол а (в радианах), величину шага для переменной orbit, конечное значение этой переменной и количество точек на орбиту, задающее число проходов цикла.
При каждом запуске программы (или при каждой перерисовке содержимого окна) графики, построенные процедурой DrawKamTorus(), будут меняться, поскольку для создания начальных значений уравнений используются случайные числа.

Множество Джулии
В отличие от фрактала Kam Torus, фрактал множество Джулии (как и рассматриваемый ниже множество Мандельброта) не использует генератор случайных чисел. Это множество порождает свой фрактал исходя из известных начальных значений. Множество Джулии может быть генерировано путем изменения в процессе описания множества Мандельброта. Для описания множества Джулии нужно начать с заданного значения C, комплексного числа (в форме a + (b * i)). Начальное значение Z также соответствует такому комплексному числу. Действительная часть данного числа соответствует координате x, а мнимая координате y, умноженной на i (мнимую единицу). Чтобы нарисовать фрактал, нужно последовательно применить уравнение Z(n+1) = Z(n)^2 + C для каждого из значений Z из ряда (0,…,n).
Для каждой точки комплексной плоскости существует свое множество Джулии, то есть существует бесконечное число различных множеств Джулии. Но наиболее интересны визуально бувают полученные из таких значений C, для которых образ М-множества (т.е. родственного точечного множества Мандельброта) наиболее плотен.

Множество Мандельброта
Фракталы, определяемые множеством Мандельброта, являются самыми известными и "знаменитыми". Это множество, как и множество Джулии рисует фрактал по его уравнению, используя комплексные числа и предопределенные отправные точки.
Несмотря на известность и всеобщее признание, множество Мандельброта остается просто графиком: горизонтальная (х) и вертикальная (у) координаты представляют области изменения двух независимых величин. В двумерном представлении для передачи различных уровней третьей величины, зависящих от двух первых, используют цвет.
Так же, как и во множестве Джулии, ось х опять представляет действительные числа, а ось у - мнимые. Итак, фрактал начинается от любой точки комплексной плоскости - C, комплексной константы. Затем берется другое комплексное число, которое уже может меняться - Z. Чтобы построить фрактал, следует начать с Z = 0 и вычислять выражение фрактала следующим образом:
Z(n) = plot
Z(n+1) = Z^2 + C
В этом выражении производится итерация функции Z(n+1) = Z(n)^2 + C. Для некоторых значений С результат через некоторое время "выравнивается". Для остальных он беспредельно растет.

Ниже прилагается исходный код файла, содержащий функции для непосредственного рисования фракталов в окне с помощью GDI. Полный же код проекта со скомпилированном exe-файлом находится в прикрепленном архиве.

#include "stdafx.h"
#include "DrawFract.h"
 
void DrawKamTorus (HWND hwnd, HDC hdc)		// Процедура рисования фрактала Kam Torus.
						// Принимает в качестве параметров дескриптор окна
						// и указатель на контекст устройства.
{
 
    int a, c, nx, ny;
    time_t t;
    unsigned long k;
    double an, can, san, can1, san1, e, r, ax, ay;
    double x, xa, x1, x2, x3, y, y1, y2, y3, rand1, rand2;
 
    HPEN DrawPen; // Указатель на перо, которым будет производиться рисование.
    RECT rect;    // Прямоугольник, необходимый для снятия размеров с клиентской области.
 
    DrawPen = CreatePen(PS_SOLID, 1, RGB(0,0,0));    // Создание пера (черное, 1 пиксель).
 
    GetClientRect(hwnd, &rect);		// Узнаем размер клиентской области.
    nx = rect.right / 2;		// Задаем опорные точки (середина окна).
    ny = rect.bottom / 2;
 
    ax = 400.0;		// Некоторые начальные значения, которые можно менять в последствии.
    ay = ax;
    c = 1;
 
    // Генерация случайных значений, служащих основой для вычмсления фрактала.
    srand((unsigned) time(&t));	
    rand1 = rand() % 20000;
    rand2 = rand() % 20000;
    rand1 = 5.0e-5*rand1;
    rand2 = 5.0e-5*rand2;
    // Вычисление пары косинусов и синусов.
    an = 10.0*(rand1-rand2);
    can = 0.99*cos(an);
    san = 0.99*sin(an);
    can1 = 1.01*cos(an);
    san1 = 1.01*sin(an);
 
    // Начальные значения для х3 и у3.
    x3 = 0.01;
    y3 = 0.01;
    e = 0.0;
    // Главный цикл.
    do {
        xa = x3*x3 - y3;
	    x2 = x3*can1 + xa*san1;
	    y2 = x3*san1 - xa*can1;
	    x3 = x2;
	    y3 = y2;
	    x = x2;
	    y = y2;
	    a = 0;
	    do {
	        xa = x*x - y;			// Расчет текущей точки.
		    x1 = x*can + xa*san;
		    y1 = x*san - xa*can;
		    x  = x1;
		    y  = y1;
		    a++;
		    MoveToEx(hdc, (int)(ax*x+nx), (int)(ay*y+ny), NULL);
		    LineTo(hdc, (int)(ax*x+nx) - 1, (int)(ay*y+ny) + 1);
	    } while ((fabs(x1)<=2.0e3) && (fabs(y1)<=2.0e3) && a <=100);
	    e = e + 0.075;
	    c = (int)e % 5 + 1;
    } while ((fabs(x2) <= 2.0e3) && (fabs(y2) <= 2.0e3));
 
}
 
void DrawJulia (HWND hwnd, HDC hdc)			// Процедура рисования Множества Джулии.
							// Принимает в качестве параметров дескриптор окна
							// и указатель на контекст устройства.
{
	// Объявление переменных для внутреннего использования при вычислениях.
	double xmin, xmax, ymin, ymax, fact = 1.0;
	double ypy, x, y, x0, y0, xp, yp, const_scr = 1.0;
	double deltax, deltay, pmin, qmin, ya, xkp1, ykp1, r;
	int npix, npiy, kcolor;
	int k, np, nq, npy, ipen;
 
	RECT rect;
 
	GetClientRect(hwnd, &rect);		// Получение размеров клиентской области окна и
	npix = rect.right;			// задание внешних границ области рисования.
	npiy = rect.bottom;
 
	pmin = -0.74356;
	qmin = 0.11135;
	xmin = -2.0;
	xmax = 2.0;
	ymin = -2.0;
	ymax = 2.0;
	kcolor = 255;
 
	if (fact >= 1.0 || fact <= 0.0)
		fact = 1.0;
	else {
		npix = (int)(npix * fact);
		npiy = (int)(npiy * fact);
	}
	ypy = (double)npiy - 0.5;
	// Переменные deltax и deltay соответсвуют разности максимальных и 
	// минимальных значений х и у, поделенной на длину экрана.
	deltax = (xmax-xmin) / (npix-1);
	deltay = (ymax-ymin) / (npiy-1);
 
	// Программа в двух циклах for - по х и у проходит по всем пикселам рисунка.
	// Переменная np будет соответствовать текущему значению х, а переменная nq - значению у.
	// При каждом проходе внешнего цикла переменной х0 присваивается значение, равное
	// минимальному значению х плюс текущее значение счетчика, умноженное на приращение х.
	for (np = 0; np <= npix - 1; np++) {
		x0 = xmin + (double)np * deltax;
		// Аналогично и во внутреннем цикле вычисляется значение у0.
		// Далее идет установка вычисленных значений в х и у и обнуление k.
		for (nq = 0; nq <= npiy - 1; nq++) {
			y0 = ymin + (double)nq * deltay;
			x = x0;
			y = y0;
			k = 0;
			// Цикл do, рисующий составляющие фрактал точки.
			// Повторяется, пока пока значения r и k не превосходят kcolor,
			// установленного ранее равным 255.
			do {
				// Вычисление действительной и мнимой части комплексного числа Z,
				// для представления которого мы двумя переменными типа double.
				// Переменная xkp1 содержит действительную часть комплексного числа,
                                // а ykp1 - мнимую.
				xkp1 = (x+y)*(x-y) + pmin;
				ya = x * y;
				ykp1 = ya + ya + qmin;
				r = xkp1*xkp1 + ykp1*ykp1; // Возведение обеих частей в квадрат и сложение.
				k++;
				// Если r больше максимального значения, то точка фрактала стремится
				// к бесконечности и закрашивается цветом, определяемым значением k
				// и выводится в позиции, заданной координатами np и nq.
				if (r <= kcolor) {		
					ipen = k;
					xp = const_scr * (double)np;
					yp = (double)nq;
					SetPixel(hdc, xp, yp, ipen);
				}
				// Если значение k равно максимальному, то точка стремится к центру,
				// она рисуется синим.
				if (k == kcolor) {
					ipen = RGB(0, 0, 255);
					xp = const_scr * (double)np;
					yp = (double)nq;
					SetPixel(hdc, xp, yp, ipen);
				}
				x = xkp1;
				y = ykp1;
			} while (r <= kcolor && k <= kcolor);
		}
	}
}
 
double MandelSetPoten (double cx, double cy, int maxiter)	// Функция, служащая для измерения
								// потенциала точки множества Мандельброта,
								// заданной параметрами cx и cy.
{
	double x, y, x2, y2, temp, potential;
	int iter;
 
	x = cx;
	x2 = x * x;
	y = cy;
	y2 = y * y;
	iter = 0;
 
	do {
		temp = x2 - y2 + cx;
		y = 2.0*x*y + cy;
		x = temp;
		x2 = x * x;
		y2 = y * y;
		iter++;
	} while ((iter < maxiter) && ((x2+y2) < 10000.0));
	if (iter < maxiter)
		potential = 0.5*log(x2+y2) / powl(2.0, iter);
	else
		potential = 0.0;
 
	return potential;
}
 
void DrawMandelbrot (HWND hwnd, HDC hdc)	// Процедура рисования Множества Мандельброта.
						// Принимает в качестве параметров дескриптор окна
						// и указатель на контекст устройства.
{
	int nx, ny, iy, ix, ipen, maxiter = 16000, iflag = 0, iset = 1;
	std::complex<double> c;
	double xmin = -2.25, ymin = -1.25, xmax = 0.75, ymax = 1.25;
	double cx, cy, potent;
	double diff = 0.6482801, test1, test2;
	if ((maxiter >= 16000) || (maxiter <= 0))
		maxiter = 16000;
	RECT rect;
	GetClientRect(hwnd, &rect);
	nx = rect.right;
	ny = rect.bottom;
	ymin = -1.125;
	ymax = 1.125;
	for (iy = 0; iy <= ny - 1; iy++) {
		cy = ymin + iy*(ymax-ymin)/(ny-1);
		for (ix = 0; ix <= nx - 1; ix++) {
			cx = xmin + ix*(xmax-xmin)/(nx-1);
			c.real(cx);
			c.imag(cy);
			test1 = 2.0;
			if ((cx >= -7.55e-1) && (cx <= 4.0e-1)) {
				if ((cy >= -6.6e-1) && (cy <= 6.6e-1))
					test1 = abs(1.0 - sqrt(1.0-4.0*c));
			}
			test2 = 2.0;
			if ((cx >= -1.275e0) && (cx <= -7.45e-1)) {
				if ((cy >= -2.55e-1) && (cy <= 2.55e-1))
					test2 = abs(4.0*(c+1.0));
			}
			if (test1 <= 1.0) {
				potent = 0;
				iflag = 1;
				if (iset != 0)
					ipen = 126;
				else
					ipen = 64;
			}
			else if (test2 <= 1.0) {
				potent = 0;
				iflag = 1;
				if (iset != 0)
					ipen = 104;
				else
					ipen = 64;
			}
			else {
				potent = MandelSetPoten(cx, cy, maxiter);
				iflag = 0;
			}
			if ((potent == 0.0) && (iflag == 0))
				ipen = 64;
			else if ((potent != 0) && (iflag == 0))
				ipen = (int)(33.0 + 15.0*(potent-33.0)/diff);
 
			SetPixel(hdc, ix, iy, ipen);
		}
	}
}

Ключевые слова: 
Рисование фракталов, Kam Torus, множество Джулии, множество Мандельброта
ВложениеРазмер
Fractals.rar1.34 Мб